[mesos/f20] Initial addition of mesos to Fedora.
tstclair
tstclair at fedoraproject.org
Tue Jan 21 18:49:35 UTC 2014
commit 1ab3ae2d13f4f683a56093118a9bac82f108ecb5
Author: Timothy St. Clair <tstclair at redhat.com>
Date: Tue Jan 21 12:49:16 2014 -0600
Initial addition of mesos to Fedora.
.gitignore | 1 +
MESOS-831.patch | 101 +
build_mods.patch | 2307 ++
fileshuffle_mods.patch |62204 ++++++++++++++++++++++++++++++++++++++++++++++++
mesos-master.service | 22 +
mesos-slave.service | 22 +
mesos-tmpfiles.conf | 1 +
mesos.spec | 239 +
sources | 1 +
testing_mods.patch | 277 +
10 files changed, 65175 insertions(+), 0 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index e69de29..07f2519 100644
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1 @@
+/mesos-0.16.0-afe9947.tar.gz
diff --git a/MESOS-831.patch b/MESOS-831.patch
new file mode 100644
index 0000000..5340a5a
--- /dev/null
+++ b/MESOS-831.patch
@@ -0,0 +1,101 @@
+diff --git a/src/cli/python/mesos/__init__.py b/src/cli/python/mesos/__init__.py
+index e69de29..ebdf877 100644
+--- a/src/cli/python/mesos/__init__.py
++++ b/src/cli/python/mesos/__init__.py
+@@ -0,0 +1,18 @@
++#!/usr/bin/env python
++
++# Licensed to the Apache Software Foundation (ASF) under one
++# or more contributor license agreements. See the NOTICE file
++# distributed with this work for additional information
++# regarding copyright ownership. The ASF licenses this file
++# to you under the Apache License, Version 2.0 (the
++# "License"); you may not use this file except in compliance
++# with the License. You may obtain a copy of the License at
++#
++# http://www.apache.org/licenses/LICENSE-2.0
++#
++# Unless required by applicable law or agreed to in writing, software
++# distributed under the License is distributed on an "AS IS" BASIS,
++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++# See the License for the specific language governing permissions and
++# limitations under the License.
++
+diff --git a/src/cli/python/mesos/cli.py b/src/cli/python/mesos/cli.py
+index 5c11d46..77c150c 100644
+--- a/src/cli/python/mesos/cli.py
++++ b/src/cli/python/mesos/cli.py
+@@ -1,3 +1,21 @@
++#!/usr/bin/env python
++
++# Licensed to the Apache Software Foundation (ASF) under one
++# or more contributor license agreements. See the NOTICE file
++# distributed with this work for additional information
++# regarding copyright ownership. The ASF licenses this file
++# to you under the Apache License, Version 2.0 (the
++# "License"); you may not use this file except in compliance
++# with the License. You may obtain a copy of the License at
++#
++# http://www.apache.org/licenses/LICENSE-2.0
++#
++# Unless required by applicable law or agreed to in writing, software
++# distributed under the License is distributed on an "AS IS" BASIS,
++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++# See the License for the specific language governing permissions and
++# limitations under the License.
++
+ # Helper for printing out a message and then the "usage" then exiting.
+ def usage(message, parser):
+ import sys
+diff --git a/src/cli/python/mesos/futures.py b/src/cli/python/mesos/futures.py
+index be374cf..933f534 100644
+--- a/src/cli/python/mesos/futures.py
++++ b/src/cli/python/mesos/futures.py
+@@ -1,3 +1,21 @@
++#!/usr/bin/env python
++
++# Licensed to the Apache Software Foundation (ASF) under one
++# or more contributor license agreements. See the NOTICE file
++# distributed with this work for additional information
++# regarding copyright ownership. The ASF licenses this file
++# to you under the Apache License, Version 2.0 (the
++# "License"); you may not use this file except in compliance
++# with the License. You may obtain a copy of the License at
++#
++# http://www.apache.org/licenses/LICENSE-2.0
++#
++# Unless required by applicable law or agreed to in writing, software
++# distributed under the License is distributed on an "AS IS" BASIS,
++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++# See the License for the specific language governing permissions and
++# limitations under the License.
++
+ try:
+ from concurrent.futures import *
+ except ImportError:
+diff --git a/src/cli/python/mesos/http.py b/src/cli/python/mesos/http.py
+index 9db9e23..a074473 100644
+--- a/src/cli/python/mesos/http.py
++++ b/src/cli/python/mesos/http.py
+@@ -1,3 +1,21 @@
++#!/usr/bin/env python
++
++# Licensed to the Apache Software Foundation (ASF) under one
++# or more contributor license agreements. See the NOTICE file
++# distributed with this work for additional information
++# regarding copyright ownership. The ASF licenses this file
++# to you under the Apache License, Version 2.0 (the
++# "License"); you may not use this file except in compliance
++# with the License. You may obtain a copy of the License at
++#
++# http://www.apache.org/licenses/LICENSE-2.0
++#
++# Unless required by applicable law or agreed to in writing, software
++# distributed under the License is distributed on an "AS IS" BASIS,
++# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++# See the License for the specific language governing permissions and
++# limitations under the License.
++
+ # Helper for doing an HTTP GET given a PID, a path, and a query dict.
+ # For example:
+ #
diff --git a/build_mods.patch b/build_mods.patch
new file mode 100644
index 0000000..ec92821
--- /dev/null
+++ b/build_mods.patch
@@ -0,0 +1,2307 @@
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/gzip.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/gzip.hpp
+index ef36f1b..bc0d818 100644
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/gzip.hpp
++++ b/3rdparty/libprocess/3rdparty/stout/include/stout/gzip.hpp
+@@ -1,9 +1,7 @@
+ #ifndef __STOUT_GZIP_HPP__
+ #define __STOUT_GZIP_HPP__
+
+-#ifdef HAVE_LIBZ
+ #include <zlib.h>
+-#endif
+
+ #include <string>
+
+@@ -26,15 +24,8 @@ namespace gzip {
+ // #define Z_DEFAULT_COMPRESSION (-1)
+ inline Try<std::string> compress(
+ const std::string& decompressed,
+-#ifdef HAVE_LIBZ
+ int level = Z_DEFAULT_COMPRESSION)
+-#else
+- int level = -1)
+-#endif
+ {
+-#ifndef HAVE_LIBZ
+- return Error("libz is not available");
+-#else
+ // Verify the level is within range.
+ if (!(level == Z_DEFAULT_COMPRESSION ||
+ (level >= Z_NO_COMPRESSION && level <= Z_BEST_COMPRESSION))) {
+@@ -88,16 +79,12 @@ inline Try<std::string> compress(
+ return Error("Failed to clean up zlib: " + std::string(stream.msg));
+ }
+ return result;
+-#endif // HAVE_LIBZ
+ }
+
+
+ // Returns a gzip decompressed version of the provided string.
+ inline Try<std::string> decompress(const std::string& compressed)
+ {
+-#ifndef HAVE_LIBZ
+- return Error("libz is not available");
+-#else
+ z_stream_s stream;
+ stream.next_in =
+ const_cast<Bytef*>(reinterpret_cast<const Bytef*>(compressed.data()));
+@@ -141,7 +128,6 @@ inline Try<std::string> decompress(const std::string& compressed)
+ return Error("Failed to clean up zlib: " + std::string(stream.msg));
+ }
+ return result;
+-#endif // HAVE_LIBZ
+ }
+
+ } // namespace gzip {
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/net.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/net.hpp
+index 1c5f88a..d03de5a 100644
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/net.hpp
++++ b/3rdparty/libprocess/3rdparty/stout/include/stout/net.hpp
+@@ -8,9 +8,7 @@
+ #include <sys/socket.h>
+ #include <sys/types.h>
+
+-#ifdef HAVE_LIBCURL
+ #include <curl/curl.h>
+-#endif
+
+ #include <string>
+
+@@ -26,9 +24,6 @@ namespace net {
+ // specified HTTP or FTP URL into a file at the specified path.
+ inline Try<int> download(const std::string& url, const std::string& path)
+ {
+-#ifndef HAVE_LIBCURL
+- return Error("libcurl is not available");
+-#else
+ Try<int> fd = os::open(
+ path, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IRWXO);
+
+@@ -70,7 +65,6 @@ inline Try<int> download(const std::string& url, const std::string& path)
+ }
+
+ return Try<int>::some(code);
+-#endif // HAVE_LIBCURL
+ }
+
+ // Returns a Try of the hostname for the provided IP. If the hostname cannot
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/nothing.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/nothing.hpp
+index c11a010..d0f925d 100644
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/nothing.hpp
++++ b/3rdparty/libprocess/3rdparty/stout/include/stout/nothing.hpp
+@@ -1,6 +1,6 @@
+-#ifndef __NOTHING_HPP__
+-#define __NOTHING_HPP__
++#ifndef __STOUT_NOTHING_HPP__
++#define __STOUT_NOTHING_HPP__
+
+ struct Nothing {};
+
+-#endif // __NOTHING_HPP__
++#endif // __STOUT_NOTHING_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/preprocessor.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/preprocessor.hpp
+index 466e16f..bd9c411 100644
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/preprocessor.hpp
++++ b/3rdparty/libprocess/3rdparty/stout/include/stout/preprocessor.hpp
+@@ -1,5 +1,5 @@
+-#ifndef __PROCESS_PREPROCESSOR_HPP__
+-#define __PROCESS_PREPROCESSOR_HPP__
++#ifndef __STOUT_PROCESS_PREPROCESSOR_HPP__
++#define __STOUT_PROCESS_PREPROCESSOR_HPP__
+
+ #include <boost/preprocessor/cat.hpp>
+
+@@ -26,4 +26,4 @@
+ #define REPEAT BOOST_PP_REPEAT
+ #define REPEAT_FROM_TO BOOST_PP_REPEAT_FROM_TO
+
+-#endif // __PROCESS_PREPROCESSOR_HPP__
++#endif // __STOUT_PROCESS_PREPROCESSOR_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/proc.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/proc.hpp
+index c3eac30..1bbbaa1 100644
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/proc.hpp
++++ b/3rdparty/libprocess/3rdparty/stout/include/stout/proc.hpp
+@@ -1,5 +1,23 @@
+-#ifndef __PROC_HPP__
+-#define __PROC_HPP__
++/**
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements. See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership. The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License. You may obtain a copy of the License at
++ *
++ * http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++
++#ifndef __STOUT_PROC_HPP__
++#define __STOUT_PROC_HPP__
+
+ // This file contains linux-only utilities for /proc.
+ #ifndef __linux__
+@@ -472,4 +490,4 @@ inline Try<std::list<CPU> > cpus()
+
+ } // namespace proc {
+
+-#endif // __PROC_HPP__
++#endif // __STOUT_PROC_HPP__
+diff --git a/3rdparty/libprocess/src/decoder.hpp b/3rdparty/libprocess/src/decoder.hpp
+index 4c29229..be410d9 100644
+--- a/3rdparty/libprocess/src/decoder.hpp
++++ b/3rdparty/libprocess/src/decoder.hpp
+@@ -28,14 +28,17 @@ public:
+ settings.on_message_begin = &DataDecoder::on_message_begin;
+ settings.on_header_field = &DataDecoder::on_header_field;
+ settings.on_header_value = &DataDecoder::on_header_value;
+- settings.on_path = &DataDecoder::on_path;
+ settings.on_url = &DataDecoder::on_url;
+- settings.on_fragment = &DataDecoder::on_fragment;
+- settings.on_query_string = &DataDecoder::on_query_string;
+ settings.on_body = &DataDecoder::on_body;
+ settings.on_headers_complete = &DataDecoder::on_headers_complete;
+ settings.on_message_complete = &DataDecoder::on_message_complete;
+
++#if !(HTTP_PARSER_VERSION_MAJOR >=2)
++ settings.on_path = &DataDecoder::on_path;
++ settings.on_fragment = &DataDecoder::on_fragment;
++ settings.on_query_string = &DataDecoder::on_query_string;
++#endif
++
+ http_parser_init(&parser, HTTP_REQUEST);
+
+ parser.data = this;
+@@ -163,19 +166,37 @@ private:
+ return 0;
+ }
+
+- static int on_path(http_parser* p, const char* data, size_t length)
++ static int on_url(http_parser* p, const char* data, size_t length)
+ {
+ DataDecoder* decoder = (DataDecoder*) p->data;
+ assert(decoder->request != NULL);
+- decoder->request->path.append(data, length);
+- return 0;
++ decoder->request->url.append(data, length);
++ int ret = 0;
++
++#if (HTTP_PARSER_VERSION_MAJOR >=2)
++ // reworked parsing for version 2.0 &>
++ http_parser_url tUrlData;
++ ret = http_parser_parse_url(data, length, 0, &tUrlData);
++
++ if (tUrlData.field_set & (1<<UF_PATH))
++ decoder->request->path.append(data+tUrlData.field_data[UF_PATH].off, tUrlData.field_data[UF_PATH].len);
++
++ if (tUrlData.field_set & (1<<UF_FRAGMENT))
++ decoder->request->fragment.append(data+tUrlData.field_data[UF_FRAGMENT].off, tUrlData.field_data[UF_FRAGMENT].len);
++
++ if (tUrlData.field_set & (1<<UF_QUERY))
++ decoder->query.append(data+tUrlData.field_data[UF_QUERY].off, tUrlData.field_data[UF_QUERY].len);
++#endif
++
++ return ret;
+ }
+
+- static int on_url(http_parser* p, const char* data, size_t length)
+- {
++#if !(HTTP_PARSER_VERSION_MAJOR >=2)
++ static int on_path(http_parser* p, const char* data, size_t length)
++ {
+ DataDecoder* decoder = (DataDecoder*) p->data;
+ assert(decoder->request != NULL);
+- decoder->request->url.append(data, length);
++ decoder->request->path.append(data, length);
+ return 0;
+ }
+
+@@ -194,6 +215,7 @@ private:
+ decoder->request->fragment.append(data, length);
+ return 0;
+ }
++#endif
+
+ static int on_body(http_parser* p, const char* data, size_t length)
+ {
+@@ -234,14 +256,17 @@ public:
+ settings.on_message_begin = &ResponseDecoder::on_message_begin;
+ settings.on_header_field = &ResponseDecoder::on_header_field;
+ settings.on_header_value = &ResponseDecoder::on_header_value;
+- settings.on_path = &ResponseDecoder::on_path;
+- settings.on_url = &ResponseDecoder::on_url;
+- settings.on_fragment = &ResponseDecoder::on_fragment;
+- settings.on_query_string = &ResponseDecoder::on_query_string;
++ settings.on_url = &ResponseDecoder::on_url;
+ settings.on_body = &ResponseDecoder::on_body;
+ settings.on_headers_complete = &ResponseDecoder::on_headers_complete;
+ settings.on_message_complete = &ResponseDecoder::on_message_complete;
+
++#if !(HTTP_PARSER_VERSION_MAJOR >=2)
++ settings.on_path = &ResponseDecoder::on_path;
++ settings.on_fragment = &ResponseDecoder::on_fragment;
++ settings.on_query_string = &ResponseDecoder::on_query_string;
++#endif
++
+ http_parser_init(&parser, HTTP_RESPONSE);
+
+ parser.data = this;
+diff --git a/Makefile.am b/Makefile.am
+index 685bd68..11befd9 100644
+--- a/Makefile.am
++++ b/Makefile.am
+@@ -18,12 +18,14 @@ ACLOCAL_AMFLAGS = -I m4
+
+ AUTOMAKE_OPTIONS = foreign
+
+-SUBDIRS = . 3rdparty src ec2
++SUBDIRS = . include src ec2
+
+ EXTRA_DIST =
+
+ PHONY_TARGETS =
+
++pkgconfigdir = $(libdir)/pkgconfig
++pkgconfig_DATA = mesos.pc
+
+ # Since we generate several files in src/ with config.status, make
+ # sure they're regenerated before we recurse into the src directory.
+diff --git a/bootstrap b/bootstrap
+index ed0bc36..de6dc73 100755
+--- a/bootstrap
++++ b/bootstrap
+@@ -43,22 +43,21 @@ fi
+
+ # Make sure that we have a version of automake that doesn't force
+ # 'subdir-objects'.
+-
+-AUTOMAKE_VERSION="$($AUTOMAKE --version | head -1 | cut -d' ' -f4)"
+-case ${AUTOMAKE_VERSION} in
+- 1.14)
+- cat >&2 <<__EOF__
+-
+-Mesos requires automake < 1.14 due to incompatibility issues
+-with the 'subdir-objects' option.
+-
+-See https://issues.apache.org/jira/browse/MESOS-577 and
+-please downgrade your automake installation.
+-
+-__EOF__
+- exit 1
+- ;;
+-esac
++#AUTOMAKE_VERSION=$(automake --version | head -1 | cut -d' ' -f4)
++#case ${AUTOMAKE_VERSION} in
++# 1.14)
++# cat >&2 <<__EOF__
++#
++#Mesos requires automake < 1.14 due to incompatibility issues
++#with the 'subdir-objects' option.
++#
++#See https://issues.apache.org/jira/browse/MESOS-577 and
++#please downgrade your automake installation.
++#
++#__EOF__
++# exit 1
++# ;;
++#esac
+
+ # Note that we don't use '--no-recursive' because older versions of
+ # autoconf/autoreconf bail with that option. Unfortunately this means
+diff --git a/configure.ac b/configure.ac
+index ba4ec1d..ac164aa 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -74,12 +74,10 @@ ac_configure_args="$ac_configure_args_post"
+ AC_CONFIG_COMMANDS_PRE([ac_configure_args="$ac_configure_args_pre"])
+ AC_CONFIG_COMMANDS_POST([ac_configure_args="$ac_configure_args_post"])
+
+-AC_CONFIG_SUBDIRS([3rdparty/libprocess])
+-
+-AC_CONFIG_FILES([Makefile])
++AC_CONFIG_FILES([Makefile mesos.pc])
+ AC_CONFIG_FILES([ec2/Makefile])
+ AC_CONFIG_FILES([src/Makefile])
+-AC_CONFIG_FILES([3rdparty/Makefile])
++AC_CONFIG_FILES([include/Makefile])
+
+ AC_CONFIG_FILES([bin/mesos.sh], [chmod +x bin/mesos.sh])
+ AC_CONFIG_FILES([bin/mesos-local.sh], [chmod +x bin/mesos-local.sh])
+@@ -131,20 +129,8 @@ AC_ARG_WITH([curl],
+ (and its dependencies) are available]),
+ [], [with_curl=yes])
+
+-AC_ARG_WITH([included-zookeeper],
+- AS_HELP_STRING([--without-included-zookeeper],
+- [excludes building and using the included ZooKeeper
+- package in lieu of a system installed version (note,
+- however, that no attempt is made to find the package
+- and explicitly setting CPPFLAGS and LDFLAGS as
+- appropriate may be necessary)]),
+- [], [with_included_zookeeper=yes])
+-
+-# TODO(benh): Support --without-included-protobuf,
+-# --without-included-glog, etc. Doing this for protobuf is
+-# considerably more tricky because we need to make sure that 'protoc'
+-# exists, that a protobuf JAR exists or we can make one, that a
+-# protobuf egg exists or we can make one, etc.
++# External zookeeper required, may want to add additional positive check
++AC_CHECK_HEADERS([zookeeper/zookeeper.h], [], [AC_MSG_ERROR([zookeeper is missing])])
+
+ AC_ARG_VAR([JAVA_HOME], [location of Java Development Kit (JDK)])
+
+@@ -162,7 +148,7 @@ case "${target_os}" in
+ echo Setting up build environment for ${target_cpu} ${target_os}
+ echo ===========================================================
+ OS_NAME=linux
+- LIBS="$LIBS -lrt"
++ #LIBS="$LIBS -lrt"
+ ;;
+ darwin*)
+ echo ===========================================================
+@@ -449,7 +435,10 @@ if test "x$enable_python" = "xyes"; then
+ AC_SUBST([PYTHON]) # Used by the example shell scripts and src/Makefile.am.
+
+ AC_DEFINE([MESOS_HAS_PYTHON])
+-
++
++ #Added check for boto module
++ AC_PYTHON_MODULE(boto, yes)
++
+ has_python=yes
+ fi
+
+@@ -466,8 +455,8 @@ fi
+
+
+ # Check if we should/can build with libcurl.
+-if test "x$with_curl" = "xyes"; then
+- AC_CHECK_LIB([z], [gzread], [],
++#if test "x$with_curl" = "xyes"; then
++ AC_CHECK_LIB([z], [gzread], [AC_MSG_RESULT([found])],
+ [AC_MSG_ERROR([cannot find libz
+ -------------------------------------------------------------------
+ We need libz for libcurl; you can avoid this with --without-curl,
+@@ -476,48 +465,79 @@ if test "x$with_curl" = "xyes"; then
+ -------------------------------------------------------------------
+ ])])
+
+- AC_CHECK_LIB([crypto], [BN_init], [],
+- [AC_MSG_ERROR([cannot find libcrypto
+- -------------------------------------------------------------------
+- We need libcrypto for libcurl; you can avoid this with
+- --without-curl, but it will mean executor and task resources cannot
+- be downloaded over http.
+- -------------------------------------------------------------------
+- ])])
+-
+- AC_CHECK_LIB([ssl], [SSL_accept], [],
+- [AC_MSG_ERROR([cannot find libssl
+- -------------------------------------------------------------------
+- We need libssl for libcurl; you can avoid this with --without-curl,
+- but it will mean executor and task resources cannot be downloaded
+- over http.
+- -------------------------------------------------------------------
+- ])])
+-
+- AC_CHECK_LIB([curl], [curl_global_init], [],
+- [AC_MSG_ERROR([cannot find libcurl
+- -------------------------------------------------------------------
+- You can avoid this with --without-curl, but it will mean executor
+- and task resources cannot be downloaded over http.
+- -------------------------------------------------------------------
+- ])])
+-
+-fi
+-
+-
+-AM_CONDITIONAL([WITH_INCLUDED_ZOOKEEPER],
+- [test "x$with_included_zookeeper" = "xyes"])
+-
++## NOTES: TSTCLAIR This linkage is unused.
++
++ #AC_CHECK_LIB([crypto], [BN_init], [AC_MSG_RESULT([found])],
++ # [AC_MSG_ERROR([cannot find libcrypto
++ #-------------------------------------------------------------------
++ #We need libcrypto for libcurl; you can avoid this with
++ #--without-curl, but it will mean executor and task resources cannot
++ #be downloaded overhttp.
++ #-------------------------------------------------------------------
++ #])])
++
++ #AC_CHECK_LIB([ssl], [SSL_accept], [AC_MSG_RESULT([found])],
++ # [AC_MSG_ERROR([cannot find libssl
++ #-------------------------------------------------------------------
++ #We need libssl for libcurl; you can avoid this with --without-curl,
++ #but it will mean executor and task resources cannot be downloaded
++ #over http.
++ #-------------------------------------------------------------------
++ #])])
++
++ #AC_CHECK_LIB([curl], [curl_global_init], [AC_MSG_RESULT([found])],
++ # [AC_MSG_ERROR([cannot find libcurl
++ #-------------------------------------------------------------------
++ #You can avoid this with --without-curl, but it will mean executor
++ #and task resources cannot be downloaded over http.
++ #-------------------------------------------------------------------
++ #])])
++#fi
++
++# Check for libev
++# rawhide has caused some issue
++# PKG_CHECK_MODULES([LIBEV], [libev], [], [AC_MSG_ERROR([libev library is missing])])
++
++# Check for leveldb - may want to be more elaborate
++AC_CHECK_HEADERS([leveldb/db.h], [], [AC_MSG_ERROR([leveldb is missing])])
++
++# Check for protobuf
++PKG_CHECK_MODULES([PROTOBUF], [protobuf], [], [AC_MSG_ERROR([protobuf library is missing])])
++
++# Check for protobuf - may want be more elaborate (compile check) or change --with
++AC_CHECK_HEADERS([google/protobuf/compiler/code_generator.h], [], [AC_MSG_ERROR([protobuf is missing])])
++
++# Check for boost
++AC_LANG_PUSH([C++])
++AC_CHECK_HEADERS([boost/foreach.hpp], [], [AC_MSG_ERROR(Boost libraries is missing)])
++AC_LANG_POP([C++])
++
++# Check for gtest
++AC_CHECK_HEADERS([gtest/gtest.h], [], [AC_MSG_ERROR([gtest library is missing])])
++
++# Check for glog
++PKG_CHECK_MODULES([GLOG], [libglog], [], [AC_MSG_ERROR([glog library is missing])])
++
++# Check for gmock
++AC_CHECK_FILE([/usr/src/gmock/gmock-all.cc],
++ [AC_CONFIG_LINKS([src/gmock-all.cc:/usr/src/gmock/gmock-all.cc])],
++ [AC_MSG_ERROR([gmock sources are missing])])
++
++AM_CONDITIONAL([HAS_GPERFTOOLS], [test "x$gperftools" = "xyes"])
++
++# Used for conditionally building source files (e.g., only want to
++# build stout/tests/proc_tests.cpp on Linux).
++AM_CONDITIONAL([OS_LINUX], [test "x$OS_NAME" = "xlinux"])
+
+ # TODO(benh): Also check for md5 support so we can use the CRAM-MD5
+ # mechanism. We can likely do a AC_CHECK_LIB looking for a particular
+ # function only provided if md5 support is present.
+-AC_CHECK_LIB([sasl2], [sasl_done], [],
+- [AC_MSG_ERROR([cannot find libsasl2
+--------------------------------------------------------------------
+-We need libsasl2 for authentication!
+--------------------------------------------------------------------
+-])])
++#AC_CHECK_LIB([sasl2], [sasl_done], [AC_MSG_RESULT([found])],
++# [AC_MSG_ERROR([cannot find libsasl2
++#-------------------------------------------------------------------
++#We need libsasl2 for authentication!
++#-------------------------------------------------------------------
++#])])
+
+
+ AC_OUTPUT
+diff --git a/include/Makefile.am b/include/Makefile.am
+new file mode 100644
+index 0000000..a7fb991
+--- /dev/null
++++ b/include/Makefile.am
+@@ -0,0 +1,102 @@
++
++nobase_include_HEADERS = mesos/mesos.proto \
++mesos/resources.hpp \
++mesos/scheduler.hpp \
++mesos/mesos.hpp \
++mesos/process/gc.hpp \
++mesos/process/once.hpp \
++mesos/process/protobuf.hpp \
++mesos/process/gmock.hpp \
++mesos/process/io.hpp \
++mesos/process/clock.hpp \
++mesos/process/statistics.hpp \
++mesos/process/timeout.hpp \
++mesos/process/id.hpp \
++mesos/process/tuples/tuples.hpp \
++mesos/process/tuples/details.hpp \
++mesos/process/message.hpp \
++mesos/process/delay.hpp \
++mesos/process/collect.hpp \
++mesos/process/event.hpp \
++mesos/process/future.hpp \
++mesos/process/help.hpp \
++mesos/process/deferred.hpp \
++mesos/process/async.hpp \
++mesos/process/gtest.hpp \
++mesos/process/run.hpp \
++mesos/process/logging.hpp \
++mesos/process/dispatch.hpp \
++mesos/process/http.hpp \
++mesos/process/limiter.hpp \
++mesos/process/owned.hpp \
++mesos/process/pid.hpp \
++mesos/process/timer.hpp \
++mesos/process/process.hpp \
++mesos/process/socket.hpp \
++mesos/process/latch.hpp \
++mesos/process/shared.hpp \
++mesos/process/defer.hpp \
++mesos/process/executor.hpp \
++mesos/process/filter.hpp \
++mesos/process/profiler.hpp \
++mesos/process/mime.hpp \
++mesos/process/time.hpp \
++mesos/stout/gzip.hpp \
++mesos/stout/flags.hpp \
++mesos/stout/cache.hpp \
++mesos/stout/protobuf.hpp \
++mesos/stout/fs.hpp \
++mesos/stout/thread.hpp \
++mesos/stout/flags/flags.hpp \
++mesos/stout/flags/flag.hpp \
++mesos/stout/flags/parse.hpp \
++mesos/stout/flags/loader.hpp \
++mesos/stout/stopwatch.hpp \
++mesos/stout/error.hpp \
++mesos/stout/fatal.hpp \
++mesos/stout/exit.hpp \
++mesos/stout/hashset.hpp \
++mesos/stout/os.hpp \
++mesos/stout/nothing.hpp \
++mesos/stout/numify.hpp \
++mesos/stout/json.hpp \
++mesos/stout/linkedhashmap.hpp \
++mesos/stout/preprocessor.hpp \
++mesos/stout/try.hpp \
++mesos/stout/lambda.hpp \
++mesos/stout/utils.hpp \
++mesos/stout/result.hpp \
++mesos/stout/gtest.hpp \
++mesos/stout/set.hpp \
++mesos/stout/path.hpp \
++mesos/stout/hashmap.hpp \
++mesos/stout/foreach.hpp \
++mesos/stout/strings.hpp \
++mesos/stout/net.hpp \
++mesos/stout/multihashmap.hpp \
++mesos/stout/proc.hpp \
++mesos/stout/bytes.hpp \
++mesos/stout/os/signals.hpp \
++mesos/stout/os/killtree.hpp \
++mesos/stout/os/linux.hpp \
++mesos/stout/os/osx.hpp \
++mesos/stout/os/ls.hpp \
++mesos/stout/os/sendfile.hpp \
++mesos/stout/os/sysctl.hpp \
++mesos/stout/os/process.hpp \
++mesos/stout/os/pstree.hpp \
++mesos/stout/os/read.hpp \
++mesos/stout/os/exists.hpp \
++mesos/stout/os/fork.hpp \
++mesos/stout/option.hpp \
++mesos/stout/stringify.hpp \
++mesos/stout/none.hpp \
++mesos/stout/uuid.hpp \
++mesos/stout/some.hpp \
++mesos/stout/format.hpp \
++mesos/stout/multimap.hpp \
++mesos/stout/duration.hpp \
++mesos/stout/check.hpp \
++mesos/executor.hpp \
++mesos/values.hpp
++
+diff --git a/m4/ac_python_module.m4 b/m4/ac_python_module.m4
+new file mode 100644
+index 0000000..32b9d72
+--- /dev/null
++++ b/m4/ac_python_module.m4
+@@ -0,0 +1,30 @@
++dnl @synopsis AC_PYTHON_MODULE(modname[, fatal])
++dnl
++dnl Checks for Python module.
++dnl
++dnl If fatal is non-empty then absence of a module will trigger an
++dnl error.
++dnl
++dnl @category InstalledPackages
++dnl @author Andrew Collier <colliera at nu.ac.za>.
++dnl @version 2004-07-14
++dnl @license AllPermissive
++
++AC_DEFUN([AC_PYTHON_MODULE],[
++ AC_MSG_CHECKING(python module: $1)
++ python -c "import $1" 2>/dev/null
++ if test $? -eq 0;
++ then
++ AC_MSG_RESULT(yes)
++ eval AS_TR_CPP(HAVE_PYMOD_$1)=yes
++ else
++ AC_MSG_RESULT(no)
++ eval AS_TR_CPP(HAVE_PYMOD_$1)=no
++ #
++ if test -n "$2"
++ then
++ AC_MSG_ERROR(failed to find required module $1)
++ exit 1
++ fi
++ fi
++])
+diff --git a/mesos.pc.in b/mesos.pc.in
+new file mode 100644
+index 0000000..06d68d5
+--- /dev/null
++++ b/mesos.pc.in
+@@ -0,0 +1,14 @@
++################################
++# Pkg-Config file for mesos #
++################################
++
++Name: mesos
++Description: Apache Mesos is a cluster manager that provides efficient
++URL: http://mesos.apache.org/
++Version: @VERSION@
++
++prefix=@prefix@
++exec_prefix=@exec_prefix@
++includedir=@includedir@
++libdir=@libdir@
++
+diff --git a/src/Makefile.am b/src/Makefile.am
+index abef3d2..ddd4e4a 100644
+--- a/src/Makefile.am
++++ b/src/Makefile.am
+@@ -19,31 +19,14 @@
+ # master/http.cpp and slave/http.cpp.
+ AUTOMAKE_OPTIONS = subdir-objects
+
+-include ../3rdparty/versions.am
+-include ../3rdparty/libprocess/3rdparty/versions.am
+-
+-# TODO(charles): Move these into an included automakefile and have
+-# them include $(top_builddir) as appropriate.
+-DISTRIBUTE = 3rdparty/distribute-$(DISTRIBUTE_VERSION)
+-LEVELDB = 3rdparty/leveldb
+-ZOOKEEPER = 3rdparty/zookeeper-$(ZOOKEEPER_VERSION)/src/c
+-LIBPROCESS = 3rdparty/libprocess
+-STOUT = $(LIBPROCESS)/3rdparty/stout
+-BOOST = $(LIBPROCESS)/3rdparty/boost-$(BOOST_VERSION)
+-GLOG = $(LIBPROCESS)/3rdparty/glog-$(GLOG_VERSION)
+-GMOCK = $(LIBPROCESS)/3rdparty/gmock-$(GMOCK_VERSION)
+-GTEST = $(GMOCK)/gtest
+-PROTOBUF = $(LIBPROCESS)/3rdparty/protobuf-$(PROTOBUF_VERSION)
+-
+-
+ # Unfortunatley, 'pkglibexecdir' and 'pkglocalstatedir' are not set
+ # before automake 1.11, so we need to set them manually (until we in
+ # the future assume an automake version).
+ pkglibexecdir = $(libexecdir)/$(PACKAGE)
+-pkglocalstatedir = $(localstatedir)/$(PACKAGE)
++pkglocalstatedir = $(sysconfdir)/$(PACKAGE)
+
+ # Protocol buffer compiler.
+-PROTOC = ../$(PROTOBUF)/src/protoc
++PROTOC = protoc
+ PROTOCFLAGS = -I$(top_srcdir)/include/mesos -I$(srcdir)
+
+ # Initialize variables here so we can use += operator everywhere else.
+@@ -76,18 +59,7 @@ MESOS_CPPFLAGS += -DLIBDIR=\"$(libdir)\"
+ MESOS_CPPFLAGS += -DPKGLIBEXECDIR=\"$(pkglibexecdir)\"
+ MESOS_CPPFLAGS += -DPKGDATADIR=\"$(pkgdatadir)\"
+ MESOS_CPPFLAGS += -I$(top_srcdir)/include
+-MESOS_CPPFLAGS += -I$(top_srcdir)/$(LIBPROCESS)/include
+-MESOS_CPPFLAGS += -I$(top_srcdir)/$(STOUT)/include
+-MESOS_CPPFLAGS += -I../include
+-MESOS_CPPFLAGS += -I../$(BOOST)
+-MESOS_CPPFLAGS += -I../$(PROTOBUF)/src
+-MESOS_CPPFLAGS += -I../$(GLOG)/src
+-
+-if WITH_INCLUDED_ZOOKEEPER
+- MESOS_CPPFLAGS += -I../$(ZOOKEEPER)/include
+- MESOS_CPPFLAGS += -I../$(ZOOKEEPER)/generated
+-endif
+-
++MESOS_CPPFLAGS += -I$(top_srcdir)/include/mesos
+
+ # README: we build the Mesos library out of a collection of
+ # convenience libraries (that is, libraries that do not get installed
+@@ -148,7 +120,7 @@ $(PYTHON_PROTOS): $(MESOS_PROTO)
+ # self-contained Python library and statically link in the third party
+ # libraries themselves.
+ noinst_LTLIBRARIES += libmesos_no_3rdparty.la
+-
++
+ nodist_libmesos_no_3rdparty_la_SOURCES = \
+ $(CXX_PROTOS) \
+ $(MESSAGES_PROTOS) \
+@@ -162,10 +134,10 @@ libmesos_no_3rdparty_la_SOURCES = \
+ sched/sched.cpp \
+ local/local.cpp \
+ master/contender.cpp \
+- master/constants.cpp \
++ master/m_constants.cpp \
+ master/detector.cpp \
+ master/drf_sorter.cpp \
+- master/http.cpp \
++ master/m_http.cpp \
+ master/master.cpp \
+ master/registry.hpp \
+ master/registry.proto \
+@@ -196,12 +168,6 @@ libmesos_no_3rdparty_la_SOURCES = \
+ zookeeper/group.cpp \
+ messages/messages.proto
+
+-pkginclude_HEADERS = $(top_srcdir)/include/mesos/executor.hpp \
+- $(top_srcdir)/include/mesos/scheduler.hpp \
+- $(top_srcdir)/include/mesos/resources.hpp \
+- $(top_srcdir)/include/mesos/values.hpp \
+- $(top_srcdir)/include/mesos/mesos.proto
+-
+ nodist_pkginclude_HEADERS = ../include/mesos/mesos.hpp mesos.pb.h
+
+ if OS_LINUX
+@@ -256,6 +222,38 @@ libmesos_no_3rdparty_la_CPPFLAGS = $(MESOS_CPPFLAGS)
+
+ libmesos_no_3rdparty_la_LIBADD = # Initialized to enable using +=.
+
++noinst_LTLIBRARIES += libprocess.la
++
++libprocess_la_SOURCES = \
++ libprocess/config.hpp \
++ libprocess/decoder.hpp \
++ libprocess/encoder.hpp \
++ libprocess/gate.hpp \
++ libprocess/latch.cpp \
++ libprocess/pid.cpp \
++ libprocess/process.cpp \
++ libprocess/statistics.cpp \
++ libprocess/synchronized.hpp
++
++libprocess_la_CPPFLAGS = \
++ -I$(top_srcdir)/include/mesos \
++ -I$(top_srcdir)/src/libprocess \
++ $(AM_CPPFLAGS)
++
++#
++# libprocess_la_LDFLAGS = \
++# -lglog \
++# -lhttp_parser \
++# -lev \
++# -lz
++#
++
++# if HAS_GPERFTOOLS
++# libprocess_la_LDFLAGS += -lprofiler
++# endif
++
++libmesos_no_3rdparty_la_LIBADD += libprocess.la
++
+ # Convenience library that *always* gets rebuilt to ensure accurate info.
+ noinst_LTLIBRARIES += libbuild.la
+ libbuild_la_SOURCES = common/build.cpp
+@@ -310,7 +308,7 @@ lib_LTLIBRARIES += libmesos.la
+
+ libmesos_la_SOURCES = $(MESOS_PROTO) # Include as part of the distribution.
+
+-libmesos_la_LDFLAGS = -release $(PACKAGE_VERSION) -shared
++libmesos_la_LDFLAGS = -version-info 0:0:0 -release $(PACKAGE_VERSION) -shared
+
+ # Since we just include the convenience library (and no sources), we
+ # need to tell libtool to build this as a C++ library.
+@@ -319,23 +317,8 @@ libmesos_la_LIBTOOLFLAGS = --tag=CXX
+ # Add the convenience library.
+ libmesos_la_LIBADD = libmesos_no_3rdparty.la
+
+-# For non-convenience libraries we need to link them in to make the shared
+-# library each time. (Currently, we don't support platforms where this is not
+-# possible.)
+-libmesos_la_LIBADD += ../$(PROTOBUF)/src/libprotobuf.la
+-libmesos_la_LIBADD += ../$(GLOG)/libglog.la
+-
+-# We need to directly include the leveldb library in order to avoid
+-# the installed libmesos.la file to include leveldb in
+-# 'dependency_libs' (via '-L../3rdparty/leveldb -lleveldb').
+-libmesos_la_LIBADD += ../$(LEVELDB)/libleveldb.a
+-
+-if WITH_INCLUDED_ZOOKEEPER
+- libmesos_la_LIBADD += ../$(ZOOKEEPER)/libzookeeper_mt.la
+-endif
+-
+-libmesos_la_LIBADD += ../$(LIBPROCESS)/libprocess.la
+-
++# TODO (tstclair) enable --with-DEP=foo
++libmesos_la_LDFLAGS += -lprotobuf -lglog -lleveldb -lzookeeper_mt -lrt -lcurl -lhttp_parser -lev
+
+ # Binaries.
+ sbin_PROGRAMS += mesos-master
+@@ -505,7 +488,8 @@ EXTRA_DIST += $(EXAMPLES_SOURCE)
+
+ if HAS_JAVA
+ # Protocol buffers JAR.
+-PROTOBUF_JAR = ../protobuf-$(PROTOBUF_VERSION).jar
++# TODO: This should be searched for in configure
++PROTOBUF_JAR = /usr/share/java/protobuf.jar
+
+ # TODO(charles): Move into 3rdparty/Makefile.am.
+ $(PROTOBUF_JAR): # TODO(charles): Specify dependencies for the jar.
+@@ -689,8 +673,7 @@ PROTOBUF_EGG = ../$(PROTOBUF)/python/dist/protobuf-$(PROTOBUF_EGG_SUFFIX)
+
+ $(PROTOBUF_EGG):
+ @echo "Building protobuf Python egg ..."
+- cd ../$(PROTOBUF)/python && \
+- PYTHONPATH=$(DISTRIBUTE_EGG) $(PYTHON) setup.py bdist_egg
++ PYTHONPATH=$(DISTRIBUTE_EGG) $(PYTHON) ./python/setup.py bdist_egg
+
+ CLEANFILES += $(PROTOBUF_EGG)
+
+@@ -781,7 +764,34 @@ balloon_executor_SOURCES = examples/balloon_executor.cpp
+ balloon_executor_CPPFLAGS = $(MESOS_CPPFLAGS)
+ balloon_executor_LDADD = libmesos.la
+
+-check_PROGRAMS += mesos-tests
++check_PROGRAMS += lptests mesos-tests
++
++lptests_SOURCES = \
++ libprocess/tests/decoder_tests.cpp \
++ libprocess/tests/encoder_tests.cpp \
++ libprocess/tests/http_tests.cpp \
++ libprocess/tests/io_tests.cpp \
++ libprocess/tests/main.cpp \
++ libprocess/tests/process_tests.cpp \
++ libprocess/tests/shared_tests.cpp \
++ libprocess/tests/statistics_tests.cpp \
++ libprocess/tests/time_tests.cpp \
++ gmock-all.cc
++
++lptests_CPPFLAGS = \
++ -I$(top_srcdir)/include/mesos \
++ -I$(top_srcdir)/src/libprocess \
++ -I/usr/src/gmock \
++ $(libprocess_la_CPPFLAGS)
++
++lptests_LDADD = \
++ -lpthread \
++ libprocess.la \
++ -lglog \
++ -lgtest \
++ -lhttp_parser \
++ -lev \
++ -lz
+
+ mesos_tests_SOURCES = \
+ tests/allocator_tests.cpp \
+@@ -815,15 +825,16 @@ mesos_tests_SOURCES = \
+ tests/state_tests.cpp \
+ tests/status_update_manager_tests.cpp \
+ tests/utils.cpp \
+- tests/zookeeper_url_tests.cpp
++ tests/zookeeper_url_tests.cpp \
++ gmock-all.cc
+
+ mesos_tests_CPPFLAGS = $(MESOS_CPPFLAGS)
+ mesos_tests_CPPFLAGS += -DSOURCE_DIR=\"$(abs_top_srcdir)\"
+ mesos_tests_CPPFLAGS += -DBUILD_DIR=\"$(abs_top_builddir)\"
+ mesos_tests_CPPFLAGS += -I../$(GTEST)/include
+-mesos_tests_CPPFLAGS += -I../$(GMOCK)/include
+-
+-mesos_tests_LDADD = ../$(LIBPROCESS)/3rdparty/libgmock.la libmesos.la
++mesos_tests_CPPFLAGS += -I/usr/src/gmock
++
++mesos_tests_LDADD = libmesos.la -lgtest -lsasl2
+
+ mesos_tests_DEPENDENCIES = # Initialized to allow += below.
+
+@@ -881,8 +892,8 @@ dist_check_SCRIPTS += \
+ # runner that ships with newer versions of autotools.
+ # See the following discussion for the workaround:
+ # http://lists.gnu.org/archive/html/automake/2013-01/msg00051.html
+-check-local: mesos-tests
+- ./mesos-tests
++check-local: mesos-tests lptests
++ ./lptests && ./mesos-tests
+
+ clean-local: clean-java clean-python
+
+diff --git a/src/deploy/mesos-daemon.sh.in b/src/deploy/mesos-daemon.sh.in
+index 2242ed1..6d019e9 100644
+--- a/src/deploy/mesos-daemon.sh.in
++++ b/src/deploy/mesos-daemon.sh.in
+@@ -3,7 +3,7 @@
+ prefix=@prefix@
+ exec_prefix=@exec_prefix@
+
+-deploy_dir=@localstatedir@/@PACKAGE@/deploy
++deploy_dir=@sysconfdir@/@PACKAGE@
+
+ # Increase the default number of open file descriptors.
+ ulimit -n 8192
+@@ -12,7 +12,6 @@ PROGRAM=${1}
+
+ shift # Remove PROGRAM from the argument list (since we pass ${@} below).
+
+-test -e ${deploy_dir}/${PROGRAM}-env.sh && \
+-. ${deploy_dir}/${PROGRAM}-env.sh
++test -e ${deploy_dir}/${PROGRAM}-env && source ${deploy_dir}/${PROGRAM}-env.sh
+
+ nohup @sbindir@/${PROGRAM} ${@} </dev/null >/dev/null 2>&1 &
+diff --git a/src/deploy/mesos-deploy-env.sh.template b/src/deploy/mesos-deploy-env.sh.template
+index afc6197..381951c 100644
+--- a/src/deploy/mesos-deploy-env.sh.template
++++ b/src/deploy/mesos-deploy-env.sh.template
+@@ -1,4 +1,3 @@
+-#!/bin/sh
+
+ # This file contains environment variables that modify how the deploy
+ # scripts are run. For example, it can be used to configure SSH
+@@ -10,4 +9,4 @@ export SSH_OPTS="-o StrictHostKeyChecking=no -o ConnectTimeout=2"
+
+
+ # Use sudo for launching masters and slaves.
+-#export DEPLOY_WITH_SUDO=1
+\ No newline at end of file
++#export DEPLOY_WITH_SUDO=1
+diff --git a/src/deploy/mesos-start-cluster.sh.in b/src/deploy/mesos-start-cluster.sh.in
+index fa27de0..210545c 100644
+--- a/src/deploy/mesos-start-cluster.sh.in
++++ b/src/deploy/mesos-start-cluster.sh.in
+@@ -3,11 +3,10 @@
+ prefix=@prefix@
+ exec_prefix=@exec_prefix@
+
+-DEPLOY_DIR=@localstatedir@/@PACKAGE@/deploy
++DEPLOY_DIR=@sysconfdir@/@PACKAGE@
+
+ # Pull in deploy specific options.
+-test -e ${DEPLOY_DIR}/mesos-deploy-env.sh && \
+- . ${DEPLOY_DIR}/mesos-deploy-env.sh
++test -e ${DEPLOY_DIR}/mesos-deploy-env && source ${DEPLOY_DIR}/mesos-deploy-env.sh
+
+ usage() {
+ echo "Usage: mesos-start-cluster.sh [-h] [-s]"
+diff --git a/src/deploy/mesos-start-masters.sh.in b/src/deploy/mesos-start-masters.sh.in
+index 3f0f524..d0f6f68 100644
+--- a/src/deploy/mesos-start-masters.sh.in
++++ b/src/deploy/mesos-start-masters.sh.in
+@@ -3,11 +3,10 @@
+ prefix=@prefix@
+ exec_prefix=@exec_prefix@
+
+-DEPLOY_DIR=@localstatedir@/@PACKAGE@/deploy
++DEPLOY_DIR=@sysconfdir@/@PACKAGE@
+
+ # Pull in deploy specific options.
+-test -e ${DEPLOY_DIR}/mesos-deploy-env.sh && \
+- . ${DEPLOY_DIR}/mesos-deploy-env.sh
++test -e ${DEPLOY_DIR}/mesos-deploy-env && source ${DEPLOY_DIR}/mesos-deploy-env.sh
+
+ # Find the list of masters.
+ MASTERS_FILE="${DEPLOY_DIR}/masters"
+diff --git a/src/deploy/mesos-start-slaves.sh.in b/src/deploy/mesos-start-slaves.sh.in
+index 7283e88..05c9717 100644
+--- a/src/deploy/mesos-start-slaves.sh.in
++++ b/src/deploy/mesos-start-slaves.sh.in
+@@ -3,11 +3,10 @@
+ prefix=@prefix@
+ exec_prefix=@exec_prefix@
+
+-DEPLOY_DIR=@localstatedir@/@PACKAGE@/deploy
++DEPLOY_DIR=@sysconfdir@/@PACKAGE@
+
+ # Pull in deploy specific options.
+-test -e ${DEPLOY_DIR}/mesos-deploy-env.sh && \
+- . ${DEPLOY_DIR}/mesos-deploy-env.sh
++test -e ${DEPLOY_DIR}/mesos-deploy-env && source ${DEPLOY_DIR}/mesos-deploy-env.sh
+
+ # Find the list of slaves.
+ SLAVES_FILE="${DEPLOY_DIR}/slaves"
+diff --git a/src/deploy/mesos-stop-masters.sh.in b/src/deploy/mesos-stop-masters.sh.in
+index 993bc64..8caa17e 100644
+--- a/src/deploy/mesos-stop-masters.sh.in
++++ b/src/deploy/mesos-stop-masters.sh.in
+@@ -2,11 +2,10 @@
+
+ prefix=@prefix@
+
+-DEPLOY_DIR=@localstatedir@/@PACKAGE@/deploy
++DEPLOY_DIR=@sysconfdir@/@PACKAGE@
+
+ # Pull in deploy specific options.
+-test -e ${DEPLOY_DIR}/mesos-deploy-env.sh && \
+- . ${DEPLOY_DIR}/mesos-deploy-env.sh
++test -e ${DEPLOY_DIR}/mesos-deploy-env && source ${DEPLOY_DIR}/mesos-deploy-env.sh
+
+ # Find the list of masters.
+ MASTERS_FILE="${DEPLOY_DIR}/masters"
+diff --git a/src/deploy/mesos-stop-slaves.sh.in b/src/deploy/mesos-stop-slaves.sh.in
+index dd0c2b8..e812c1c 100644
+--- a/src/deploy/mesos-stop-slaves.sh.in
++++ b/src/deploy/mesos-stop-slaves.sh.in
+@@ -1,11 +1,10 @@
+ #!/bin/sh
+
+ prefix=@prefix@
+-DEPLOY_DIR=@localstatedir@/@PACKAGE@/deploy
++DEPLOY_DIR=@sysconfdir@/@PACKAGE@
+
+ # Pull in deploy specific options.
+-test -e ${DEPLOY_DIR}/mesos-deploy-env.sh && \
+- . ${DEPLOY_DIR}/mesos-deploy-env.sh
++test -e ${DEPLOY_DIR}/mesos-deploy-env && source ${DEPLOY_DIR}/mesos-deploy-env.sh
+
+ # Find the list of slaves.
+ SLAVES_FILE="${DEPLOY_DIR}/slaves"
+diff --git a/src/examples/java/test-framework.in b/src/examples/java/test-framework.in
+index 251a758..1e232b2 100644
+--- a/src/examples/java/test-framework.in
++++ b/src/examples/java/test-framework.in
+@@ -12,7 +12,8 @@ JAVA=${JAVA-${JAVA_HOME}/bin/java}
+ # Use colors for errors.
+ . ${MESOS_SOURCE_DIR}/support/colors.sh
+
+-PROTOBUF_JAR=${MESOS_BUILD_DIR}/protobuf-2.4.1.jar
++# TODO:tstclair - this should be discovered using --with
++PROTOBUF_JAR=/usr/share/java/protobuf.jar
+
+ test ! -e ${PROTOBUF_JAR} && \
+ echo "${RED}Failed to find ${PROTOBUF_JAR}${NORMAL}" && \
+diff --git a/src/examples/python/test-executor.in b/src/examples/python/test-executor.in
+index 6f18682..615ed96 100644
+--- a/src/examples/python/test-executor.in
++++ b/src/examples/python/test-executor.in
+@@ -14,19 +14,6 @@ test ! -z "${PYTHON}" && \
+
+ PYTHON=@PYTHON@
+
+-DISTRIBUTE_EGG=`echo ${MESOS_BUILD_DIR}/3rdparty/distribute-*/dist/*.egg`
+-
+-test ! -e ${DISTRIBUTE_EGG} && \
+- echo "${RED}Failed to find ${DISTRIBUTE_EGG}${NORMAL}" && \
+- exit 1
+-
+-PROTOBUF=${MESOS_BUILD_DIR}/3rdparty/libprocess/3rdparty/protobuf-2.4.1
+-PROTOBUF_EGG=`echo ${PROTOBUF}/python/dist/protobuf*.egg`
+-
+-test ! -e ${PROTOBUF_EGG} && \
+- echo "${RED}Failed to find ${PROTOBUF_EGG}${NORMAL}" && \
+- exit 1
+-
+ MESOS_EGG=`echo ${MESOS_BUILD_DIR}/src/python/dist/mesos*.egg`
+
+ test ! -e ${MESOS_EGG} && \
+@@ -39,5 +26,5 @@ test ! -e ${SCRIPT} && \
+ echo "${RED}Failed to find ${SCRIPT}${NORMAL}" && \
+ exit 1
+
+-PYTHONPATH="${DISTRIBUTE_EGG}:${MESOS_EGG}:${PROTOBUF_EGG}" \
++PYTHONPATH="${MESOS_EGG}" \
+ exec ${PYTHON} ${SCRIPT} "${@}"
+diff --git a/src/examples/python/test-framework.in b/src/examples/python/test-framework.in
+index d66cf6b..ba86362 100644
+--- a/src/examples/python/test-framework.in
++++ b/src/examples/python/test-framework.in
+@@ -14,19 +14,6 @@ test ! -z "${PYTHON}" && \
+
+ PYTHON=@PYTHON@
+
+-DISTRIBUTE_EGG=`echo ${MESOS_BUILD_DIR}/3rdparty/distribute-*/dist/*.egg`
+-
+-test ! -e ${DISTRIBUTE_EGG} && \
+- echo "${RED}Failed to find ${DISTRIBUTE_EGG}${NORMAL}" && \
+- exit 1
+-
+-PROTOBUF=${MESOS_BUILD_DIR}/3rdparty/libprocess/3rdparty/protobuf-2.4.1
+-PROTOBUF_EGG=`echo ${PROTOBUF}/python/dist/protobuf*.egg`
+-
+-test ! -e ${PROTOBUF_EGG} && \
+- echo "${RED}Failed to find ${PROTOBUF_EGG}${NORMAL}" && \
+- exit 1
+-
+ MESOS_EGG=`echo ${MESOS_BUILD_DIR}/src/python/dist/mesos*.egg`
+
+ test ! -e ${MESOS_EGG} && \
+@@ -43,5 +30,5 @@ test ! -e ${SCRIPT} && \
+ # framework is able to find the executor.
+ cd `dirname ${0}`
+
+-PYTHONPATH="${DISTRIBUTE_EGG}:${MESOS_EGG}:${PROTOBUF_EGG}" \
++PYTHONPATH="${MESOS_EGG}" \
+ exec ${PYTHON} ${SCRIPT} "${@}"
+diff --git a/src/jvm/org/apache/zookeeper.hpp b/src/jvm/org/apache/zookeeper.hpp
+index dac1456..80f98b9 100644
+--- a/src/jvm/org/apache/zookeeper.hpp
++++ b/src/jvm/org/apache/zookeeper.hpp
+@@ -113,18 +113,20 @@ public:
+ {
+ static Jvm::Constructor constructor = Jvm::get()->findConstructor(
+ Jvm::Class::named(
+- "org/apache/zookeeper/server/NIOServerCnxn$Factory")
+- .constructor()
+- .parameter(Jvm::Class::named("java/net/InetSocketAddress")));
++ "org/apache/zookeeper/server/NIOServerCnxnFactory")
++ .constructor());
+
+- object = Jvm::get()->invoke(constructor, (jobject) addr);
++ //TODO may need to #if version this
++
++ object = Jvm::get()->invoke(constructor);
++ this->configure(addr);
+ }
+
+ void startup(const ZooKeeperServer& zks)
+ {
+ static Jvm::Method method = Jvm::get()->findMethod(
+ Jvm::Class::named(
+- "org/apache/zookeeper/server/NIOServerCnxn$Factory")
++ "org/apache/zookeeper/server/NIOServerCnxnFactory")
+ .method("startup")
+ .parameter(Jvm::Class::named(
+ "org/apache/zookeeper/server/ZooKeeperServer"))
+@@ -133,6 +135,19 @@ public:
+ Jvm::get()->invoke<void>(object, method, (jobject) zks);
+ }
+
++ void configure(const java::net::InetSocketAddress& addr, const int maxcc=0)
++ {
++ static Jvm::Method method = Jvm::get()->findMethod(
++ Jvm::Class::named(
++ "org/apache/zookeeper/server/NIOServerCnxnFactory")
++ .method("configure")
++ .parameter(Jvm::Class::named("java/net/InetSocketAddress"))
++ .parameter(Jvm::get()->intClass)
++ .returns(Jvm::get()->voidClass));
++
++ Jvm::get()->invoke<void>(object, method, (jobject) addr, maxcc);
++ }
++#if 0
+ bool isAlive()
+ {
+ static Jvm::Method method = Jvm::get()->findMethod(
+@@ -143,12 +158,13 @@ public:
+
+ return Jvm::get()->invoke<bool>(object, method);
+ }
+-
++#endif
++
+ void shutdown()
+ {
+ static Jvm::Method method = Jvm::get()->findMethod(
+ Jvm::Class::named(
+- "org/apache/zookeeper/server/NIOServerCnxn$Factory")
++ "org/apache/zookeeper/server/NIOServerCnxnFactory")
+ .method("shutdown")
+ .returns(Jvm::get()->voidClass));
+
+diff --git a/src/master/constants.cpp b/src/master/constants.cpp
+deleted file mode 100644
+index 4475a0d..0000000
+--- a/src/master/constants.cpp
++++ /dev/null
+@@ -1,38 +0,0 @@
+-/**
+- * Licensed to the Apache Software Foundation (ASF) under one
+- * or more contributor license agreements. See the NOTICE file
+- * distributed with this work for additional information
+- * regarding copyright ownership. The ASF licenses this file
+- * to you under the Apache License, Version 2.0 (the
+- * "License"); you may not use this file except in compliance
+- * with the License. You may obtain a copy of the License at
+- *
+- * http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-#include <stout/bytes.hpp>
+-
+-#include "master/constants.hpp"
+-
+-namespace mesos {
+-namespace internal {
+-namespace master {
+-
+-const int MAX_OFFERS_PER_FRAMEWORK = 50;
+-const double MIN_CPUS = 0.1;
+-const Bytes MIN_MEM = Megabytes(32);
+-const Duration SLAVE_PING_TIMEOUT = Seconds(15);
+-const uint32_t MAX_SLAVE_PING_TIMEOUTS = 5;
+-const uint32_t MAX_COMPLETED_FRAMEWORKS = 50;
+-const uint32_t MAX_COMPLETED_TASKS_PER_FRAMEWORK = 1000;
+-const Duration WHITELIST_WATCH_INTERVAL = Seconds(5);
+-
+-} // namespace mesos {
+-} // namespace internal {
+-} // namespace master {
+diff --git a/src/master/http.cpp b/src/master/http.cpp
+deleted file mode 100644
+index 218906a..0000000
+--- a/src/master/http.cpp
++++ /dev/null
+@@ -1,461 +0,0 @@
+-/**
+- * Licensed to the Apache Software Foundation (ASF) under one
+- * or more contributor license agreements. See the NOTICE file
+- * distributed with this work for additional information
+- * regarding copyright ownership. The ASF licenses this file
+- * to you under the Apache License, Version 2.0 (the
+- * "License"); you may not use this file except in compliance
+- * with the License. You may obtain a copy of the License at
+- *
+- * http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-#include <iomanip>
+-#include <map>
+-#include <sstream>
+-#include <string>
+-#include <vector>
+-
+-#include <mesos/mesos.hpp>
+-#include <mesos/resources.hpp>
+-
+-#include <process/help.hpp>
+-
+-#include <stout/foreach.hpp>
+-#include <stout/json.hpp>
+-#include <stout/net.hpp>
+-#include <stout/numify.hpp>
+-#include <stout/os.hpp>
+-#include <stout/result.hpp>
+-#include <stout/strings.hpp>
+-
+-#include "common/attributes.hpp"
+-#include "common/build.hpp"
+-#include "common/type_utils.hpp"
+-#include "common/protobuf_utils.hpp"
+-
+-#include "logging/logging.hpp"
+-
+-#include "master/master.hpp"
+-
+-namespace mesos {
+-namespace internal {
+-namespace master {
+-
+-using process::Future;
+-using process::HELP;
+-using process::TLDR;
+-using process::USAGE;
+-
+-using process::http::BadRequest;
+-using process::http::InternalServerError;
+-using process::http::NotFound;
+-using process::http::OK;
+-using process::http::TemporaryRedirect;
+-using process::http::Response;
+-using process::http::Request;
+-
+-using std::map;
+-using std::string;
+-using std::vector;
+-
+-// TODO(bmahler): Kill these in favor of automatic Proto->JSON Conversion (when
+-// it becomes available).
+-
+-// Returns a JSON object modeled on a Resources.
+-JSON::Object model(const Resources& resources)
+-{
+- JSON::Object object;
+-
+- foreach (const Resource& resource, resources) {
+- switch (resource.type()) {
+- case Value::SCALAR:
+- object.values[resource.name()] = resource.scalar().value();
+- break;
+- case Value::RANGES:
+- object.values[resource.name()] = stringify(resource.ranges());
+- break;
+- case Value::SET:
+- object.values[resource.name()] = stringify(resource.set());
+- break;
+- default:
+- LOG(FATAL) << "Unexpected Value type: " << resource.type();
+- break;
+- }
+- }
+-
+- return object;
+-}
+-
+-
+-JSON::Object model(const Attributes& attributes)
+-{
+- JSON::Object object;
+-
+- foreach (const Attribute& attribute, attributes) {
+- switch (attribute.type()) {
+- case Value::SCALAR:
+- object.values[attribute.name()] = attribute.scalar().value();
+- break;
+- case Value::RANGES:
+- object.values[attribute.name()] = stringify(attribute.ranges());
+- break;
+- case Value::SET:
+- object.values[attribute.name()] = stringify(attribute.set());
+- break;
+- case Value::TEXT:
+- object.values[attribute.name()] = attribute.text().value();
+- break;
+- default:
+- LOG(FATAL) << "Unexpected Value type: " << attribute.type();
+- break;
+- }
+- }
+-
+- return object;
+-}
+-
+-
+-// Returns a JSON object modeled on a TaskStatus.
+-JSON::Object model(const TaskStatus& status)
+-{
+- JSON::Object object;
+- object.values["state"] = TaskState_Name(status.state());
+- object.values["timestamp"] = status.timestamp();
+-
+- return object;
+-}
+-
+-
+-// Returns a JSON object modeled on a Task.
+-// TODO(bmahler): Expose the executor name / source.
+-JSON::Object model(const Task& task)
+-{
+- JSON::Object object;
+- object.values["id"] = task.task_id().value();
+- object.values["name"] = task.name();
+- object.values["framework_id"] = task.framework_id().value();
+- object.values["executor_id"] = task.executor_id().value();
+- object.values["slave_id"] = task.slave_id().value();
+- object.values["state"] = TaskState_Name(task.state());
+- object.values["resources"] = model(task.resources());
+-
+- JSON::Array array;
+- foreach (const TaskStatus& status, task.statuses()) {
+- array.values.push_back(model(status));
+- }
+- object.values["statuses"] = array;
+-
+- return object;
+-}
+-
+-
+-// Returns a JSON object modeled on an Offer.
+-JSON::Object model(const Offer& offer)
+-{
+- JSON::Object object;
+- object.values["id"] = offer.id().value();
+- object.values["framework_id"] = offer.framework_id().value();
+- object.values["slave_id"] = offer.slave_id().value();
+- object.values["resources"] = model(offer.resources());
+- return object;
+-}
+-
+-
+-// Returns a JSON object modeled on a Framework.
+-JSON::Object model(const Framework& framework)
+-{
+- JSON::Object object;
+- object.values["id"] = framework.id.value();
+- object.values["name"] = framework.info.name();
+- object.values["user"] = framework.info.user();
+- object.values["registered_time"] = framework.registeredTime.secs();
+- object.values["unregistered_time"] = framework.unregisteredTime.secs();
+- object.values["active"] = framework.active;
+- object.values["resources"] = model(framework.resources);
+-
+- // TODO(benh): Consider making reregisteredTime an Option.
+- if (framework.registeredTime != framework.reregisteredTime) {
+- object.values["reregistered_time"] = framework.reregisteredTime.secs();
+- }
+-
+- // Model all of the tasks associated with a framework.
+- {
+- JSON::Array array;
+- foreachvalue (Task* task, framework.tasks) {
+- array.values.push_back(model(*task));
+- }
+-
+- object.values["tasks"] = array;
+- }
+-
+- // Model all of the completed tasks of a framework.
+- {
+- JSON::Array array;
+- foreach (const Task& task, framework.completedTasks) {
+- array.values.push_back(model(task));
+- }
+-
+- object.values["completed_tasks"] = array;
+- }
+-
+- // Model all of the offers associated with a framework.
+- {
+- JSON::Array array;
+- foreach (Offer* offer, framework.offers) {
+- array.values.push_back(model(*offer));
+- }
+-
+- object.values["offers"] = array;
+- }
+-
+- return object;
+-}
+-
+-
+-// Returns a JSON object modeled after a Slave.
+-JSON::Object model(const Slave& slave)
+-{
+- JSON::Object object;
+- object.values["id"] = slave.id.value();
+- object.values["pid"] = string(slave.pid);
+- object.values["hostname"] = slave.info.hostname();
+- object.values["registered_time"] = slave.registeredTime.secs();
+-
+- if (slave.reregisteredTime.isSome()) {
+- object.values["reregistered_time"] = slave.reregisteredTime.get().secs();
+- }
+-
+- object.values["resources"] = model(slave.info.resources());
+- object.values["attributes"] = model(slave.info.attributes());
+- return object;
+-}
+-
+-// Returns a JSON object modeled after a Role.
+-JSON::Object model(const Role& role)
+-{
+- JSON::Object object;
+- object.values["name"] = role.info.name();
+- object.values["weight"] = role.info.weight();
+- object.values["resources"] = model(role.resources());
+-
+- {
+- JSON::Array array;
+-
+- foreachkey (const FrameworkID& frameworkId, role.frameworks) {
+- array.values.push_back(frameworkId.value());
+- }
+-
+- object.values["frameworks"] = array;
+- }
+-
+- return object;
+-}
+-
+-
+-const string Master::Http::HEALTH_HELP = HELP(
+- TLDR(
+- "Health check of the Master."),
+- USAGE(
+- "/master/health"),
+- DESCRIPTION(
+- "Returns 200 OK iff the Master is healthy.",
+- "Delayed responses are also indicative of poor health."));
+-
+-
+-Future<Response> Master::Http::health(const Request& request)
+-{
+- return OK();
+-}
+-
+-
+-const string Master::Http::REDIRECT_HELP = HELP(
+- TLDR(
+- "Redirects to the leading Master."),
+- USAGE(
+- "/master/redirect"),
+- DESCRIPTION(
+- "This returns a 307 Temporary Redirect to the leading Master.",
+- "If no Master is leading (according to this Master), then the",
+- "Master will redirect to itself.",
+- "",
+- "**NOTES:**",
+- "1. This is the recommended way to bookmark the WebUI when",
+- "running multiple Masters.",
+- "2. This is broken currently \"on the cloud\" (e.g. EC2) as",
+- "this will attempt to redirect to the private IP address."));
+-
+-
+-
+-Future<Response> Master::Http::redirect(const Request& request)
+-{
+- LOG(INFO) << "HTTP request for '" << request.path << "'";
+-
+- // If there's no leader, redirect to this master's base url.
+- UPID pid = master.leader.isSome() ? master.leader.get() : master.self();
+-
+- Try<string> hostname = net::getHostname(pid.ip);
+- if (hostname.isError()) {
+- return InternalServerError(hostname.error());
+- }
+-
+- return TemporaryRedirect(
+- "http://" + hostname.get() + ":" + stringify(pid.port));
+-}
+-
+-
+-Future<Response> Master::Http::stats(const Request& request)
+-{
+- LOG(INFO) << "HTTP request for '" << request.path << "'";
+-
+- JSON::Object object;
+- object.values["uptime"] = (Clock::now() - master.startTime).secs();
+- object.values["elected"] = master.elected(); // Note: using int not bool.
+- object.values["total_schedulers"] = master.frameworks.size();
+- object.values["active_schedulers"] = master.getActiveFrameworks().size();
+- object.values["activated_slaves"] = master.slaves.size();
+- object.values["deactivated_slaves"] = master.deactivatedSlaves.size();
+- object.values["staged_tasks"] = master.stats.tasks[TASK_STAGING];
+- object.values["started_tasks"] = master.stats.tasks[TASK_STARTING];
+- object.values["finished_tasks"] = master.stats.tasks[TASK_FINISHED];
+- object.values["killed_tasks"] = master.stats.tasks[TASK_KILLED];
+- object.values["failed_tasks"] = master.stats.tasks[TASK_FAILED];
+- object.values["lost_tasks"] = master.stats.tasks[TASK_LOST];
+- object.values["valid_status_updates"] = master.stats.validStatusUpdates;
+- object.values["invalid_status_updates"] = master.stats.invalidStatusUpdates;
+- object.values["outstanding_offers"] = master.offers.size();
+-
+- // Get total and used (note, not offered) resources in order to
+- // compute capacity of scalar resources.
+- Resources totalResources;
+- Resources usedResources;
+- foreachvalue (Slave* slave, master.slaves) {
+- // Instead of accumulating all types of resources (which is
+- // not necessary), we only accumulate scalar resources. This
+- // helps us bypass a performance problem caused by range
+- // additions (e.g. ports).
+- foreach (const Resource& resource, slave->info.resources()) {
+- if (resource.type() == Value::SCALAR) {
+- totalResources += resource;
+- }
+- }
+- foreach (const Resource& resource, slave->resourcesInUse) {
+- if (resource.type() == Value::SCALAR) {
+- usedResources += resource;
+- }
+- }
+- }
+-
+- foreach (const Resource& resource, totalResources) {
+- CHECK(resource.has_scalar());
+- double total = resource.scalar().value();
+- object.values[resource.name() + "_total"] = total;
+- Option<Resource> option = usedResources.get(resource);
+- CHECK(!option.isSome() || option.get().has_scalar());
+- double used = option.isSome() ? option.get().scalar().value() : 0.0;
+- object.values[resource.name() + "_used"] = used;
+- double percent = used / total;
+- object.values[resource.name() + "_percent"] = percent;
+- }
+-
+- return OK(object, request.query.get("jsonp"));
+-}
+-
+-
+-Future<Response> Master::Http::state(const Request& request)
+-{
+- LOG(INFO) << "HTTP request for '" << request.path << "'";
+-
+- JSON::Object object;
+- object.values["version"] = MESOS_VERSION;
+- object.values["build_date"] = build::DATE;
+- object.values["build_time"] = build::TIME;
+- object.values["build_user"] = build::USER;
+- object.values["start_time"] = master.startTime.secs();
+- object.values["id"] = master.info.id();
+- object.values["pid"] = string(master.self());
+- object.values["activated_slaves"] = master.slaves.size();
+- object.values["deactivated_slaves"] = master.deactivatedSlaves.size();
+- object.values["staged_tasks"] = master.stats.tasks[TASK_STAGING];
+- object.values["started_tasks"] = master.stats.tasks[TASK_STARTING];
+- object.values["finished_tasks"] = master.stats.tasks[TASK_FINISHED];
+- object.values["killed_tasks"] = master.stats.tasks[TASK_KILLED];
+- object.values["failed_tasks"] = master.stats.tasks[TASK_FAILED];
+- object.values["lost_tasks"] = master.stats.tasks[TASK_LOST];
+-
+- if (master.flags.cluster.isSome()) {
+- object.values["cluster"] = master.flags.cluster.get();
+- }
+-
+- if (master.leader.isSome()) {
+- object.values["leader"] = string(master.leader.get());
+- }
+-
+- if (master.flags.log_dir.isSome()) {
+- object.values["log_dir"] = master.flags.log_dir.get();
+- }
+-
+- // Model all of the slaves.
+- {
+- JSON::Array array;
+- foreachvalue (Slave* slave, master.slaves) {
+- array.values.push_back(model(*slave));
+- }
+-
+- object.values["slaves"] = array;
+- }
+-
+- // Model all of the frameworks.
+- {
+- JSON::Array array;
+- foreachvalue (Framework* framework, master.frameworks) {
+- array.values.push_back(model(*framework));
+- }
+-
+- object.values["frameworks"] = array;
+- }
+-
+- // Model all of the completed frameworks.
+- {
+- JSON::Array array;
+-
+- foreach (const std::tr1::shared_ptr<Framework>& framework,
+- master.completedFrameworks) {
+- array.values.push_back(model(*framework));
+- }
+-
+- object.values["completed_frameworks"] = array;
+- }
+-
+- return OK(object, request.query.get("jsonp"));
+-}
+-
+-
+-Future<Response> Master::Http::roles(const Request& request)
+-{
+- LOG(INFO) << "HTTP request for '" << request.path << "'";
+-
+- JSON::Object object;
+-
+- // Model all of the roles.
+- {
+- JSON::Array array;
+- foreachvalue (Role* role, master.roles) {
+- array.values.push_back(model(*role));
+- }
+-
+- object.values["roles"] = array;
+- }
+-
+- return OK(object, request.query.get("jsonp"));
+-}
+-
+-} // namespace master {
+-} // namespace internal {
+-} // namespace mesos {
+diff --git a/src/master/m_constants.cpp b/src/master/m_constants.cpp
+new file mode 100644
+index 0000000..4475a0d
+--- /dev/null
++++ b/src/master/m_constants.cpp
+@@ -0,0 +1,38 @@
++/**
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements. See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership. The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License. You may obtain a copy of the License at
++ *
++ * http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++
++#include <stout/bytes.hpp>
++
++#include "master/constants.hpp"
++
++namespace mesos {
++namespace internal {
++namespace master {
++
++const int MAX_OFFERS_PER_FRAMEWORK = 50;
++const double MIN_CPUS = 0.1;
++const Bytes MIN_MEM = Megabytes(32);
++const Duration SLAVE_PING_TIMEOUT = Seconds(15);
++const uint32_t MAX_SLAVE_PING_TIMEOUTS = 5;
++const uint32_t MAX_COMPLETED_FRAMEWORKS = 50;
++const uint32_t MAX_COMPLETED_TASKS_PER_FRAMEWORK = 1000;
++const Duration WHITELIST_WATCH_INTERVAL = Seconds(5);
++
++} // namespace mesos {
++} // namespace internal {
++} // namespace master {
+diff --git a/src/master/m_http.cpp b/src/master/m_http.cpp
+new file mode 100644
+index 0000000..218906a
+--- /dev/null
++++ b/src/master/m_http.cpp
+@@ -0,0 +1,461 @@
++/**
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements. See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership. The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License. You may obtain a copy of the License at
++ *
++ * http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++
++#include <iomanip>
++#include <map>
++#include <sstream>
++#include <string>
++#include <vector>
++
++#include <mesos/mesos.hpp>
++#include <mesos/resources.hpp>
++
++#include <process/help.hpp>
++
++#include <stout/foreach.hpp>
++#include <stout/json.hpp>
++#include <stout/net.hpp>
++#include <stout/numify.hpp>
++#include <stout/os.hpp>
++#include <stout/result.hpp>
++#include <stout/strings.hpp>
++
++#include "common/attributes.hpp"
++#include "common/build.hpp"
++#include "common/type_utils.hpp"
++#include "common/protobuf_utils.hpp"
++
++#include "logging/logging.hpp"
++
++#include "master/master.hpp"
++
++namespace mesos {
++namespace internal {
++namespace master {
++
++using process::Future;
++using process::HELP;
++using process::TLDR;
++using process::USAGE;
++
++using process::http::BadRequest;
++using process::http::InternalServerError;
++using process::http::NotFound;
++using process::http::OK;
++using process::http::TemporaryRedirect;
++using process::http::Response;
++using process::http::Request;
++
++using std::map;
++using std::string;
++using std::vector;
++
++// TODO(bmahler): Kill these in favor of automatic Proto->JSON Conversion (when
++// it becomes available).
++
++// Returns a JSON object modeled on a Resources.
++JSON::Object model(const Resources& resources)
++{
++ JSON::Object object;
++
++ foreach (const Resource& resource, resources) {
++ switch (resource.type()) {
++ case Value::SCALAR:
++ object.values[resource.name()] = resource.scalar().value();
++ break;
++ case Value::RANGES:
++ object.values[resource.name()] = stringify(resource.ranges());
++ break;
++ case Value::SET:
++ object.values[resource.name()] = stringify(resource.set());
++ break;
++ default:
++ LOG(FATAL) << "Unexpected Value type: " << resource.type();
++ break;
++ }
++ }
++
++ return object;
++}
++
++
++JSON::Object model(const Attributes& attributes)
++{
++ JSON::Object object;
++
++ foreach (const Attribute& attribute, attributes) {
++ switch (attribute.type()) {
++ case Value::SCALAR:
++ object.values[attribute.name()] = attribute.scalar().value();
++ break;
++ case Value::RANGES:
++ object.values[attribute.name()] = stringify(attribute.ranges());
++ break;
++ case Value::SET:
++ object.values[attribute.name()] = stringify(attribute.set());
++ break;
++ case Value::TEXT:
++ object.values[attribute.name()] = attribute.text().value();
++ break;
++ default:
++ LOG(FATAL) << "Unexpected Value type: " << attribute.type();
++ break;
++ }
++ }
++
++ return object;
++}
++
++
++// Returns a JSON object modeled on a TaskStatus.
++JSON::Object model(const TaskStatus& status)
++{
++ JSON::Object object;
++ object.values["state"] = TaskState_Name(status.state());
++ object.values["timestamp"] = status.timestamp();
++
++ return object;
++}
++
++
++// Returns a JSON object modeled on a Task.
++// TODO(bmahler): Expose the executor name / source.
++JSON::Object model(const Task& task)
++{
++ JSON::Object object;
++ object.values["id"] = task.task_id().value();
++ object.values["name"] = task.name();
++ object.values["framework_id"] = task.framework_id().value();
++ object.values["executor_id"] = task.executor_id().value();
++ object.values["slave_id"] = task.slave_id().value();
++ object.values["state"] = TaskState_Name(task.state());
++ object.values["resources"] = model(task.resources());
++
++ JSON::Array array;
++ foreach (const TaskStatus& status, task.statuses()) {
++ array.values.push_back(model(status));
++ }
++ object.values["statuses"] = array;
++
++ return object;
++}
++
++
++// Returns a JSON object modeled on an Offer.
++JSON::Object model(const Offer& offer)
++{
++ JSON::Object object;
++ object.values["id"] = offer.id().value();
++ object.values["framework_id"] = offer.framework_id().value();
++ object.values["slave_id"] = offer.slave_id().value();
++ object.values["resources"] = model(offer.resources());
++ return object;
++}
++
++
++// Returns a JSON object modeled on a Framework.
++JSON::Object model(const Framework& framework)
++{
++ JSON::Object object;
++ object.values["id"] = framework.id.value();
++ object.values["name"] = framework.info.name();
++ object.values["user"] = framework.info.user();
++ object.values["registered_time"] = framework.registeredTime.secs();
++ object.values["unregistered_time"] = framework.unregisteredTime.secs();
++ object.values["active"] = framework.active;
++ object.values["resources"] = model(framework.resources);
++
++ // TODO(benh): Consider making reregisteredTime an Option.
++ if (framework.registeredTime != framework.reregisteredTime) {
++ object.values["reregistered_time"] = framework.reregisteredTime.secs();
++ }
++
++ // Model all of the tasks associated with a framework.
++ {
++ JSON::Array array;
++ foreachvalue (Task* task, framework.tasks) {
++ array.values.push_back(model(*task));
++ }
++
++ object.values["tasks"] = array;
++ }
++
++ // Model all of the completed tasks of a framework.
++ {
++ JSON::Array array;
++ foreach (const Task& task, framework.completedTasks) {
++ array.values.push_back(model(task));
++ }
++
++ object.values["completed_tasks"] = array;
++ }
++
++ // Model all of the offers associated with a framework.
++ {
++ JSON::Array array;
++ foreach (Offer* offer, framework.offers) {
++ array.values.push_back(model(*offer));
++ }
++
++ object.values["offers"] = array;
++ }
++
++ return object;
++}
++
++
++// Returns a JSON object modeled after a Slave.
++JSON::Object model(const Slave& slave)
++{
++ JSON::Object object;
++ object.values["id"] = slave.id.value();
++ object.values["pid"] = string(slave.pid);
++ object.values["hostname"] = slave.info.hostname();
++ object.values["registered_time"] = slave.registeredTime.secs();
++
++ if (slave.reregisteredTime.isSome()) {
++ object.values["reregistered_time"] = slave.reregisteredTime.get().secs();
++ }
++
++ object.values["resources"] = model(slave.info.resources());
++ object.values["attributes"] = model(slave.info.attributes());
++ return object;
++}
++
++// Returns a JSON object modeled after a Role.
++JSON::Object model(const Role& role)
++{
++ JSON::Object object;
++ object.values["name"] = role.info.name();
++ object.values["weight"] = role.info.weight();
++ object.values["resources"] = model(role.resources());
++
++ {
++ JSON::Array array;
++
++ foreachkey (const FrameworkID& frameworkId, role.frameworks) {
++ array.values.push_back(frameworkId.value());
++ }
++
++ object.values["frameworks"] = array;
++ }
++
++ return object;
++}
++
++
++const string Master::Http::HEALTH_HELP = HELP(
++ TLDR(
++ "Health check of the Master."),
++ USAGE(
++ "/master/health"),
++ DESCRIPTION(
++ "Returns 200 OK iff the Master is healthy.",
++ "Delayed responses are also indicative of poor health."));
++
++
++Future<Response> Master::Http::health(const Request& request)
++{
++ return OK();
++}
++
++
++const string Master::Http::REDIRECT_HELP = HELP(
++ TLDR(
++ "Redirects to the leading Master."),
++ USAGE(
++ "/master/redirect"),
++ DESCRIPTION(
++ "This returns a 307 Temporary Redirect to the leading Master.",
++ "If no Master is leading (according to this Master), then the",
++ "Master will redirect to itself.",
++ "",
++ "**NOTES:**",
++ "1. This is the recommended way to bookmark the WebUI when",
++ "running multiple Masters.",
++ "2. This is broken currently \"on the cloud\" (e.g. EC2) as",
++ "this will attempt to redirect to the private IP address."));
++
++
++
++Future<Response> Master::Http::redirect(const Request& request)
++{
++ LOG(INFO) << "HTTP request for '" << request.path << "'";
++
++ // If there's no leader, redirect to this master's base url.
++ UPID pid = master.leader.isSome() ? master.leader.get() : master.self();
++
++ Try<string> hostname = net::getHostname(pid.ip);
++ if (hostname.isError()) {
++ return InternalServerError(hostname.error());
++ }
++
++ return TemporaryRedirect(
++ "http://" + hostname.get() + ":" + stringify(pid.port));
++}
++
++
++Future<Response> Master::Http::stats(const Request& request)
++{
++ LOG(INFO) << "HTTP request for '" << request.path << "'";
++
++ JSON::Object object;
++ object.values["uptime"] = (Clock::now() - master.startTime).secs();
++ object.values["elected"] = master.elected(); // Note: using int not bool.
++ object.values["total_schedulers"] = master.frameworks.size();
++ object.values["active_schedulers"] = master.getActiveFrameworks().size();
++ object.values["activated_slaves"] = master.slaves.size();
++ object.values["deactivated_slaves"] = master.deactivatedSlaves.size();
++ object.values["staged_tasks"] = master.stats.tasks[TASK_STAGING];
++ object.values["started_tasks"] = master.stats.tasks[TASK_STARTING];
++ object.values["finished_tasks"] = master.stats.tasks[TASK_FINISHED];
++ object.values["killed_tasks"] = master.stats.tasks[TASK_KILLED];
++ object.values["failed_tasks"] = master.stats.tasks[TASK_FAILED];
++ object.values["lost_tasks"] = master.stats.tasks[TASK_LOST];
++ object.values["valid_status_updates"] = master.stats.validStatusUpdates;
++ object.values["invalid_status_updates"] = master.stats.invalidStatusUpdates;
++ object.values["outstanding_offers"] = master.offers.size();
++
++ // Get total and used (note, not offered) resources in order to
++ // compute capacity of scalar resources.
++ Resources totalResources;
++ Resources usedResources;
++ foreachvalue (Slave* slave, master.slaves) {
++ // Instead of accumulating all types of resources (which is
++ // not necessary), we only accumulate scalar resources. This
++ // helps us bypass a performance problem caused by range
++ // additions (e.g. ports).
++ foreach (const Resource& resource, slave->info.resources()) {
++ if (resource.type() == Value::SCALAR) {
++ totalResources += resource;
++ }
++ }
++ foreach (const Resource& resource, slave->resourcesInUse) {
++ if (resource.type() == Value::SCALAR) {
++ usedResources += resource;
++ }
++ }
++ }
++
++ foreach (const Resource& resource, totalResources) {
++ CHECK(resource.has_scalar());
++ double total = resource.scalar().value();
++ object.values[resource.name() + "_total"] = total;
++ Option<Resource> option = usedResources.get(resource);
++ CHECK(!option.isSome() || option.get().has_scalar());
++ double used = option.isSome() ? option.get().scalar().value() : 0.0;
++ object.values[resource.name() + "_used"] = used;
++ double percent = used / total;
++ object.values[resource.name() + "_percent"] = percent;
++ }
++
++ return OK(object, request.query.get("jsonp"));
++}
++
++
++Future<Response> Master::Http::state(const Request& request)
++{
++ LOG(INFO) << "HTTP request for '" << request.path << "'";
++
++ JSON::Object object;
++ object.values["version"] = MESOS_VERSION;
++ object.values["build_date"] = build::DATE;
++ object.values["build_time"] = build::TIME;
++ object.values["build_user"] = build::USER;
++ object.values["start_time"] = master.startTime.secs();
++ object.values["id"] = master.info.id();
++ object.values["pid"] = string(master.self());
++ object.values["activated_slaves"] = master.slaves.size();
++ object.values["deactivated_slaves"] = master.deactivatedSlaves.size();
++ object.values["staged_tasks"] = master.stats.tasks[TASK_STAGING];
++ object.values["started_tasks"] = master.stats.tasks[TASK_STARTING];
++ object.values["finished_tasks"] = master.stats.tasks[TASK_FINISHED];
++ object.values["killed_tasks"] = master.stats.tasks[TASK_KILLED];
++ object.values["failed_tasks"] = master.stats.tasks[TASK_FAILED];
++ object.values["lost_tasks"] = master.stats.tasks[TASK_LOST];
++
++ if (master.flags.cluster.isSome()) {
++ object.values["cluster"] = master.flags.cluster.get();
++ }
++
++ if (master.leader.isSome()) {
++ object.values["leader"] = string(master.leader.get());
++ }
++
++ if (master.flags.log_dir.isSome()) {
++ object.values["log_dir"] = master.flags.log_dir.get();
++ }
++
++ // Model all of the slaves.
++ {
++ JSON::Array array;
++ foreachvalue (Slave* slave, master.slaves) {
++ array.values.push_back(model(*slave));
++ }
++
++ object.values["slaves"] = array;
++ }
++
++ // Model all of the frameworks.
++ {
++ JSON::Array array;
++ foreachvalue (Framework* framework, master.frameworks) {
++ array.values.push_back(model(*framework));
++ }
++
++ object.values["frameworks"] = array;
++ }
++
++ // Model all of the completed frameworks.
++ {
++ JSON::Array array;
++
++ foreach (const std::tr1::shared_ptr<Framework>& framework,
++ master.completedFrameworks) {
++ array.values.push_back(model(*framework));
++ }
++
++ object.values["completed_frameworks"] = array;
++ }
++
++ return OK(object, request.query.get("jsonp"));
++}
++
++
++Future<Response> Master::Http::roles(const Request& request)
++{
++ LOG(INFO) << "HTTP request for '" << request.path << "'";
++
++ JSON::Object object;
++
++ // Model all of the roles.
++ {
++ JSON::Array array;
++ foreachvalue (Role* role, master.roles) {
++ array.values.push_back(model(*role));
++ }
++
++ object.values["roles"] = array;
++ }
++
++ return OK(object, request.query.get("jsonp"));
++}
++
++} // namespace master {
++} // namespace internal {
++} // namespace mesos {
+diff --git a/src/python/setup.py.in b/src/python/setup.py.in
+index 77fa880..8d17fc1 100644
+--- a/src/python/setup.py.in
++++ b/src/python/setup.py.in
+@@ -19,21 +19,6 @@ abs_top_builddir = '@abs_top_builddir@'
+ src_python_dist = os.path.join('src', 'python', 'dist')
+ src_python_native = os.path.join('src', 'python', 'native')
+
+-leveldb = os.path.join('3rdparty', 'leveldb')
+-zookeeper = os.path.join('3rdparty', 'zookeeper-3.3.4', 'src', 'c')
+-libprocess = os.path.join('3rdparty', 'libprocess')
+-
+-# Even though a statically compiled libprocess should include glog,
+-# libev, gperftools, and protobuf before installation this isn't the
+-# case, so while a libtool managed build will correctly pull in these
+-# libraries when building the final result, we need to explicitly
+-# include them here (or more precisely, down where we actually include
+-# libev.a and libprofiler.a).
+-glog = os.path.join(libprocess, '3rdparty', 'glog-0.3.3')
+-libev = os.path.join(libprocess, '3rdparty', 'libev-4.15')
+-gperftools = os.path.join(libprocess, '3rdparty', 'gperftools-2.0')
+-protobuf = os.path.join(libprocess, '3rdparty', 'protobuf-2.4.1')
+-
+ # We need to execute from the same directory as this script.
+ os.chdir(os.path.abspath(os.path.dirname(__file__)))
+
+@@ -63,26 +48,25 @@ INCLUDE_DIRS = [
+ os.path.join(abs_top_srcdir, 'include'),
+ os.path.join(abs_top_builddir, 'include'),
+ os.path.join(abs_top_builddir, 'src'),
+- os.path.join(abs_top_builddir, src_python_native),
+- os.path.join(abs_top_builddir, protobuf, 'src'),
++ os.path.join(abs_top_builddir, src_python_native)
+ ]
+
+ LIBRARY_DIRS = []
+
++# TSTCLAIR: I still debate if this is unkosher.
++
+ EXTRA_OBJECTS = [
+ os.path.join(abs_top_builddir, 'src', '.libs', 'libmesos_no_3rdparty.a'),
+- os.path.join(abs_top_builddir, protobuf, 'src', '.libs', 'libprotobuf.a'),
+- os.path.join(abs_top_builddir, leveldb, 'libleveldb.a'),
+- os.path.join(abs_top_builddir, zookeeper, '.libs', 'libzookeeper_mt.a'),
+- os.path.join(abs_top_builddir, libprocess, '.libs', 'libprocess.a'),
+- os.path.join(abs_top_builddir, glog, '.libs', 'libglog.a'),
+- os.path.join(abs_top_builddir, libev, '.libs', 'libev.a'),
++ '-lprotobuf',
++ '-lleveldb',
++ '-lzookeeper_mt',
++ '-lglog',
++ '-lev',
+ ]
+
+ # For gperftools, we need to check for the presence of libprofiler.a, since
+ # it is possible to disable perftools inside libprocess.
+-libprofiler = os.path.join(
+- abs_top_builddir, gperftools, '.libs', 'libprofiler.a')
++libprofiler = '-lprofiler'
+
+ if os.path.exists(libprofiler):
+ EXTRA_OBJECTS.append(libprofiler)
+diff --git a/src/zookeeper/authentication.hpp b/src/zookeeper/authentication.hpp
+index 7b6b767..4adbccf 100644
+--- a/src/zookeeper/authentication.hpp
++++ b/src/zookeeper/authentication.hpp
+@@ -1,7 +1,7 @@
+ #ifndef __ZOOKEEPER_AUTHENTICATION_HPP__
+ #define __ZOOKEEPER_AUTHENTICATION_HPP__
+
+-#include <zookeeper.h>
++#include <zookeeper/zookeeper.h>
+
+ #include <string>
+
+diff --git a/src/zookeeper/zookeeper.hpp b/src/zookeeper/zookeeper.hpp
+index 7243543..26accb4 100644
+--- a/src/zookeeper/zookeeper.hpp
++++ b/src/zookeeper/zookeeper.hpp
+@@ -26,7 +26,7 @@
+ #ifndef ZOOKEEPER_HPP
+ #define ZOOKEEPER_HPP
+
+-#include <zookeeper.h>
++#include <zookeeper/zookeeper.h>
+
+ #include <string>
+ #include <vector>
diff --git a/fileshuffle_mods.patch b/fileshuffle_mods.patch
new file mode 100644
index 0000000..475cf04
--- /dev/null
+++ b/fileshuffle_mods.patch
@@ -0,0 +1,62204 @@
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/bytes.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/bytes.hpp
+deleted file mode 100644
+index 754fbb2..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/bytes.hpp
++++ /dev/null
+@@ -1,160 +0,0 @@
+-#ifndef __STOUT_BYTES_HPP__
+-#define __STOUT_BYTES_HPP__
+-
+-#include <ctype.h> // For 'isdigit'.
+-#include <stdint.h>
+-
+-#include <iomanip>
+-#include <iostream>
+-#include <string>
+-
+-#include "numify.hpp"
+-#include "strings.hpp"
+-#include "try.hpp"
+-
+-
+-class Bytes
+-{
+-public:
+- static Try<Bytes> parse(const std::string& s)
+- {
+- size_t index = 0;
+-
+- while (index < s.size()) {
+- if (isdigit(s[index])) {
+- index++;
+- continue;
+- } else if (s[index] == '.') {
+- return Error("Fractional bytes '" + s + "'");
+- }
+-
+- Try<uint64_t> value = numify<uint64_t>(s.substr(0, index));
+-
+- if (value.isError()) {
+- return Error(value.error());
+- }
+-
+- const std::string& unit = strings::upper(s.substr(index));
+-
+- if (unit == "B") {
+- return Bytes(value.get(), BYTES);
+- } else if (unit == "KB") {
+- return Bytes(value.get(), KILOBYTES);
+- } else if (unit == "MB") {
+- return Bytes(value.get(), MEGABYTES);
+- } else if (unit == "GB") {
+- return Bytes(value.get(), GIGABYTES);
+- } else if (unit == "TB") {
+- return Bytes(value.get(), TERABYTES);
+- } else {
+- return Error("Unknown bytes unit '" + unit + "'");
+- }
+- }
+- return Error("Invalid bytes '" + s + "'");
+- }
+-
+- Bytes(uint64_t bytes = 0) : value(bytes) {}
+- Bytes(uint64_t _value, uint64_t _unit) : value(_value * _unit) {}
+-
+- // TODO(bmahler): Consider killing kilobytes to terabyte helpers, given
+- // they implicitly lose precision if not careful.
+- uint64_t bytes() const { return value; }
+- uint64_t kilobytes() const { return value / KILOBYTES; }
+- uint64_t megabytes() const { return value / MEGABYTES; }
+- uint64_t gigabytes() const { return value / GIGABYTES; }
+- uint64_t terabytes() const { return value / TERABYTES; }
+-
+- bool operator < (const Bytes& that) const { return value < that.value; }
+- bool operator <= (const Bytes& that) const { return value <= that.value; }
+- bool operator > (const Bytes& that) const { return value > that.value; }
+- bool operator >= (const Bytes& that) const { return value >= that.value; }
+- bool operator == (const Bytes& that) const { return value == that.value; }
+- bool operator != (const Bytes& that) const { return value != that.value; }
+-
+- Bytes& operator += (const Bytes& that)
+- {
+- value += that.value;
+- return *this;
+- }
+-
+- Bytes& operator -= (const Bytes& that)
+- {
+- value -= that.value;
+- return *this;
+- }
+-
+-protected:
+- static const uint64_t BYTES = 1;
+- static const uint64_t KILOBYTES = 1024 * BYTES;
+- static const uint64_t MEGABYTES = 1024 * KILOBYTES;
+- static const uint64_t GIGABYTES = 1024 * MEGABYTES;
+- static const uint64_t TERABYTES = 1024 * GIGABYTES;
+-
+-private:
+- uint64_t value;
+-};
+-
+-
+-class Kilobytes : public Bytes
+-{
+-public:
+- explicit Kilobytes(uint64_t value) : Bytes(value, KILOBYTES) {}
+-};
+-
+-
+-class Megabytes : public Bytes
+-{
+-public:
+- explicit Megabytes(uint64_t value) : Bytes(value, MEGABYTES) {}
+-};
+-
+-
+-class Gigabytes : public Bytes
+-{
+-public:
+- explicit Gigabytes(uint64_t value) : Bytes(value, GIGABYTES) {}
+-};
+-
+-
+-class Terabytes : public Bytes
+-{
+-public:
+- explicit Terabytes(uint64_t value) : Bytes(value, TERABYTES) {}
+-};
+-
+-
+-inline std::ostream& operator << (std::ostream& stream, const Bytes& bytes)
+-{
+- // Only raise the unit when there is no loss of information.
+- if (bytes.bytes() == 0) {
+- return stream << bytes.bytes() << "B";
+- } else if (bytes.bytes() % 1024 != 0) {
+- return stream << bytes.bytes() << "B";
+- } else if (bytes.kilobytes() % 1024 != 0) {
+- return stream << bytes.kilobytes() << "KB";
+- } else if (bytes.megabytes() % 1024 != 0) {
+- return stream << bytes.megabytes() << "MB";
+- } else if (bytes.gigabytes() % 1024 != 0) {
+- return stream << bytes.gigabytes() << "GB";
+- } else {
+- return stream << bytes.terabytes() << "TB";
+- }
+-}
+-
+-
+-inline Bytes operator + (const Bytes& lhs, const Bytes& rhs)
+-{
+- Bytes sum = lhs;
+- sum += rhs;
+- return sum;
+-}
+-
+-
+-inline Bytes operator - (const Bytes& lhs, const Bytes& rhs)
+-{
+- Bytes diff = lhs;
+- diff -= rhs;
+- return diff;
+-}
+-
+-#endif // __STOUT_BYTES_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/cache.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/cache.hpp
+deleted file mode 100644
+index 653507c..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/cache.hpp
++++ /dev/null
+@@ -1,131 +0,0 @@
+-#ifndef __STOUT_CACHE_HPP__
+-#define __STOUT_CACHE_HPP__
+-
+-#include <functional>
+-#include <iostream>
+-#include <list>
+-#include <map>
+-
+-#include <tr1/functional>
+-#include <tr1/unordered_map>
+-
+-#include "none.hpp"
+-#include "option.hpp"
+-
+-// Forward declaration.
+-template <typename Key, typename Value>
+-class cache;
+-
+-// Outputs the key/value pairs from least to most-recently used.
+-template <typename Key, typename Value>
+-std::ostream& operator << (
+- std::ostream& stream,
+- const cache<Key, Value>& c);
+-
+-
+-// Provides a least-recently used (LRU) cache of some predefined
+-// capacity. A "write" and a "read" both count as uses.
+-template <typename Key, typename Value>
+-class cache
+-{
+-public:
+- typedef std::list<Key> list;
+- typedef std::tr1::unordered_map<
+- Key, std::pair<Value, typename list::iterator> > map;
+-
+- explicit cache(int _capacity) : capacity(_capacity) {}
+-
+- void put(const Key& key, const Value& value)
+- {
+- typename map::iterator i = values.find(key);
+- if (i == values.end()) {
+- insert(key, value);
+- } else {
+- (*i).second.first = value;
+- use(i);
+- }
+- }
+-
+- Option<Value> get(const Key& key)
+- {
+- typename map::iterator i = values.find(key);
+-
+- if (i != values.end()) {
+- use(i);
+- return (*i).second.first;
+- }
+-
+- return None();
+- }
+-
+-private:
+- // Not copyable, not assignable.
+- cache(const cache&);
+- cache& operator = (const cache&);
+-
+- // Give the operator access to our internals.
+- friend std::ostream& operator << <>(
+- std::ostream& stream,
+- const cache<Key, Value>& c);
+-
+- // Insert key/value into the cache.
+- void insert(const Key& key, const Value& value)
+- {
+- if (keys.size() == capacity) {
+- evict();
+- }
+-
+- // Get a "pointer" into the lru list for efficient update.
+- typename list::iterator i = keys.insert(keys.end(), key);
+-
+- // Save key/value and "pointer" into lru list.
+- values.insert(std::make_pair(key, std::make_pair(value, i)));
+- }
+-
+- // Updates the LRU ordering in the cache for the given iterator.
+- void use(const typename map::iterator& i)
+- {
+- // Move the "pointer" to the end of the lru list.
+- keys.splice(keys.end(), keys, (*i).second.second);
+-
+- // Now update the "pointer" so we can do this again.
+- (*i).second.second = --keys.end();
+- }
+-
+- // Evict the least-recently used element from the cache.
+- void evict()
+- {
+- const typename map::iterator& i = values.find(keys.front());
+- CHECK(i != values.end());
+- values.erase(i);
+- keys.pop_front();
+- }
+-
+- // Size of the cache.
+- int capacity;
+-
+- // Cache of values and "pointers" into the least-recently used list.
+- map values;
+-
+- // Keys ordered by least-recently used.
+- list keys;
+-};
+-
+-
+-template <typename Key, typename Value>
+-std::ostream& operator << (
+- std::ostream& stream,
+- const cache<Key, Value>& c)
+-{
+- typename cache<Key, Value>::list::const_iterator i1;
+- for (i1 = c.keys.begin(); i1 != c.keys.end(); i1++) {
+- stream << *i1 << ": ";
+- typename cache<Key, Value>::map::const_iterator i2;
+- i2 = c.values.find(*i1);
+- CHECK(i2 != c.values.end());
+- stream << *i2 << std::endl;
+- }
+- return stream;
+-}
+-
+-#endif // __STOUT_CACHE_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/check.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/check.hpp
+deleted file mode 100644
+index eb31841..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/check.hpp
++++ /dev/null
+@@ -1,90 +0,0 @@
+-#ifndef __STOUT_CHECK_HPP__
+-#define __STOUT_CHECK_HPP__
+-
+-#include <ostream>
+-#include <sstream>
+-#include <string>
+-
+-#include <glog/logging.h> // Includes LOG(*), PLOG(*), CHECK, etc.
+-
+-#include <stout/none.hpp>
+-#include <stout/option.hpp>
+-#include <stout/result.hpp>
+-#include <stout/some.hpp>
+-#include <stout/try.hpp>
+-
+-// Provides a CHECK_SOME macro, akin to CHECK.
+-// This appends the error if possible to the end of the log message, so there's
+-// no need to append the error message explicitly.
+-#define CHECK_SOME(expression) \
+- for (const Option<std::string>& _error = _check(expression); \
+- _error.isSome();) \
+- _CheckSome(__FILE__, __LINE__, #expression, _error.get()).stream() \
+-
+-// Private structs/functions used for CHECK_SOME.
+-
+-template <typename T>
+-Option<std::string> _check(const Option<T>& o)
+-{
+- if (o.isNone()) {
+- return Some("is NONE");
+- }
+- return None();
+-}
+-
+-
+-template <typename T>
+-Option<std::string> _check(const Try<T>& t)
+-{
+- if (t.isError()) {
+- return t.error();
+- }
+- return None();
+-}
+-
+-
+-template <typename T>
+-Option<std::string> _check(const Result<T>& r)
+-{
+- if (r.isError()) {
+- return r.error();
+- } else if (r.isNone()) {
+- return Some("is NONE");
+- }
+- return None();
+-}
+-
+-
+-struct _CheckSome
+-{
+- _CheckSome(const char* _file,
+- int _line,
+- const char* _expression,
+- const std::string& _error)
+- : file(_file),
+- line(_line),
+- expression(_expression),
+- error(_error)
+- {
+- out << "CHECK_SOME(" << expression << "): ";
+- }
+-
+- ~_CheckSome()
+- {
+- out << error;
+- google::LogMessageFatal(file.c_str(), line).stream() << out.str();
+- }
+-
+- std::ostream& stream()
+- {
+- return out;
+- }
+-
+- const std::string file;
+- const int line;
+- const std::string expression;
+- const std::string error;
+- std::ostringstream out;
+-};
+-
+-#endif // __STOUT_CHECK_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/duration.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/duration.hpp
+deleted file mode 100644
+index 2f5a93e..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/duration.hpp
++++ /dev/null
+@@ -1,357 +0,0 @@
+-#ifndef __STOUT_DURATION_HPP__
+-#define __STOUT_DURATION_HPP__
+-
+-#include <ctype.h> // For 'isdigit'.
+-#include <limits.h> // For 'LLONG_(MAX|MIN)'
+-
+-#include <iomanip>
+-#include <iostream>
+-#include <string>
+-
+-#include "error.hpp"
+-#include "numify.hpp"
+-#include "try.hpp"
+-
+-class Duration
+-{
+-public:
+- static Try<Duration> parse(const std::string& s)
+- {
+- // TODO(benh): Support negative durations (i.e., starts with '-').
+- size_t index = 0;
+- while (index < s.size()) {
+- if (isdigit(s[index]) || s[index] == '.') {
+- index++;
+- continue;
+- }
+-
+- Try<double> value = numify<double>(s.substr(0, index));
+-
+- if (value.isError()) {
+- return Error(value.error());
+- }
+-
+- const std::string& unit = s.substr(index);
+-
+- if (unit == "ns") {
+- return Duration(value.get(), NANOSECONDS);
+- } else if (unit == "us") {
+- return Duration(value.get(), MICROSECONDS);
+- } else if (unit == "ms") {
+- return Duration(value.get(), MILLISECONDS);
+- } else if (unit == "secs") {
+- return Duration(value.get(), SECONDS);
+- } else if (unit == "mins") {
+- return Duration(value.get(), MINUTES);
+- } else if (unit == "hrs") {
+- return Duration(value.get(), HOURS);
+- } else if (unit == "days") {
+- return Duration(value.get(), DAYS);
+- } else if (unit == "weeks") {
+- return Duration(value.get(), WEEKS);
+- } else {
+- return Error("Unknown duration unit '" + unit + "'");
+- }
+- }
+- return Error("Invalid duration '" + s + "'");
+- }
+-
+- static Try<Duration> create(double seconds);
+-
+- Duration() : nanos(0) {}
+-
+- int64_t ns() const { return nanos; }
+- double us() const { return static_cast<double>(nanos) / MICROSECONDS; }
+- double ms() const { return static_cast<double>(nanos) / MILLISECONDS; }
+- double secs() const { return static_cast<double>(nanos) / SECONDS; }
+- double mins() const { return static_cast<double>(nanos) / MINUTES; }
+- double hrs() const { return static_cast<double>(nanos) / HOURS; }
+- double days() const { return static_cast<double>(nanos) / DAYS; }
+- double weeks() const { return static_cast<double>(nanos) / WEEKS; }
+-
+- bool operator < (const Duration& d) const { return nanos < d.nanos; }
+- bool operator <= (const Duration& d) const { return nanos <= d.nanos; }
+- bool operator > (const Duration& d) const { return nanos > d.nanos; }
+- bool operator >= (const Duration& d) const { return nanos >= d.nanos; }
+- bool operator == (const Duration& d) const { return nanos == d.nanos; }
+- bool operator != (const Duration& d) const { return nanos != d.nanos; }
+-
+- Duration& operator += (const Duration& that)
+- {
+- nanos += that.nanos;
+- return *this;
+- }
+-
+- Duration& operator -= (const Duration& that)
+- {
+- nanos -= that.nanos;
+- return *this;
+- }
+-
+- Duration& operator *= (double multiplier)
+- {
+- nanos = static_cast<int64_t>(nanos * multiplier);
+- return *this;
+- }
+-
+- Duration& operator /= (double divisor)
+- {
+- nanos = static_cast<int64_t>(nanos / divisor);
+- return *this;
+- }
+-
+- Duration operator + (const Duration& that) const
+- {
+- Duration sum = *this;
+- sum += that;
+- return sum;
+- }
+-
+- Duration operator - (const Duration& that) const
+- {
+- Duration diff = *this;
+- diff -= that;
+- return diff;
+- }
+-
+- Duration operator * (double multiplier) const
+- {
+- Duration product = *this;
+- product *= multiplier;
+- return product;
+- }
+-
+- Duration operator / (double divisor) const
+- {
+- Duration quotient = *this;
+- quotient /= divisor;
+- return quotient;
+- }
+-
+- // TODO(xujyan): Use constexpr for the following variables after
+- // switching to C++11.
+- // A constant holding the maximum value a Duration can have.
+- static Duration max();
+- // A constant holding the minimum (negative) value a Duration can
+- // have.
+- static Duration min();
+- // A constant holding a Duration of a "zero" value.
+- static Duration zero() { return Duration(); }
+-
+-protected:
+- static const int64_t NANOSECONDS = 1;
+- static const int64_t MICROSECONDS = 1000 * NANOSECONDS;
+- static const int64_t MILLISECONDS = 1000 * MICROSECONDS;
+- static const int64_t SECONDS = 1000 * MILLISECONDS;
+- static const int64_t MINUTES = 60 * SECONDS;
+- static const int64_t HOURS = 60 * MINUTES;
+- static const int64_t DAYS = 24 * HOURS;
+- static const int64_t WEEKS = 7 * DAYS;
+-
+- // For the Seconds, Minutes, Hours, Days & Weeks constructor.
+- Duration(int32_t value, int64_t unit)
+- : nanos(value * unit) {}
+-
+- // For the Nanoseconds, Microseconds, Milliseconds constructor.
+- Duration(int64_t value, int64_t unit)
+- : nanos(value * unit) {}
+-
+-private:
+- // Used only by "parse".
+- Duration(double value, int64_t unit)
+- : nanos(static_cast<int64_t>(value * unit)) {}
+-
+- int64_t nanos;
+-
+- friend std::ostream& operator << (
+- std::ostream& stream,
+- const Duration& duration);
+-};
+-
+-
+-class Nanoseconds : public Duration
+-{
+-public:
+- explicit Nanoseconds(int64_t nanoseconds)
+- : Duration(nanoseconds, NANOSECONDS) {}
+-
+- Nanoseconds(const Duration& d) : Duration(d) {}
+-};
+-
+-
+-class Microseconds : public Duration
+-{
+-public:
+- explicit Microseconds(int64_t microseconds)
+- : Duration(microseconds, MICROSECONDS) {}
+-
+- Microseconds(const Duration& d) : Duration(d) {}
+-};
+-
+-
+-class Milliseconds : public Duration
+-{
+-public:
+- explicit Milliseconds(int64_t milliseconds)
+- : Duration(milliseconds, MILLISECONDS) {}
+-
+- Milliseconds(const Duration& d) : Duration(d) {}
+-};
+-
+-
+-class Seconds : public Duration
+-{
+-public:
+- explicit Seconds(int64_t seconds)
+- : Duration(seconds, SECONDS) {}
+-
+- Seconds(const Duration& d) : Duration(d) {}
+-};
+-
+-
+-class Minutes : public Duration
+-{
+-public:
+- explicit Minutes(int32_t minutes)
+- : Duration(minutes, MINUTES) {}
+-
+- Minutes(const Duration& d) : Duration(d) {}
+-};
+-
+-
+-class Hours : public Duration
+-{
+-public:
+- explicit Hours(int32_t hours)
+- : Duration(hours, HOURS) {}
+-
+- Hours(const Duration& d) : Duration(d) {}
+-};
+-
+-
+-class Days : public Duration
+-{
+-public:
+- explicit Days(int32_t days)
+- : Duration(days, DAYS) {}
+-
+- Days(const Duration& d) : Duration(d) {}
+-};
+-
+-
+-class Weeks : public Duration
+-{
+-public:
+- explicit Weeks(int32_t value) : Duration(value, WEEKS) {}
+-
+- Weeks(const Duration& d) : Duration(d) {}
+-};
+-
+-
+-inline std::ostream& operator << (
+- std::ostream& stream,
+- const Duration& duration_)
+-{
+- long precision = stream.precision();
+-
+- // Output the duration in full double precision.
+- stream.precision(std::numeric_limits<double>::digits10);
+-
+- // Parse the duration as the sign and the absolute value.
+- Duration duration = duration_;
+- if (duration_ < Duration::zero()) {
+- stream << "-";
+-
+- // Duration::min() may not be representable as a positive Duration.
+- if (duration_ == Duration::min()) {
+- duration = Duration::max();
+- } else {
+- duration = duration_ * -1;
+- }
+- }
+-
+- // First determine which bucket of time unit the duration falls into
+- // then check whether the duration can be represented as a whole
+- // number with this time unit or a smaller one.
+- // e.g. 1.42857142857143weeks falls into the 'Weeks' bucket but
+- // reads better with a smaller unit: '10days'. So we use 'days'
+- // instead of 'weeks' to output the duration.
+- int64_t nanoseconds = duration.ns();
+- if (duration < Microseconds(1)) {
+- stream << duration.ns() << "ns";
+- } else if (duration < Milliseconds(1)) {
+- if (nanoseconds % Duration::MICROSECONDS != 0) {
+- // We can't get a whole number using this unit but we can at
+- // one level down.
+- stream << duration.ns() << "ns";
+- } else {
+- stream << duration.us() << "us";
+- }
+- } else if (duration < Seconds(1)) {
+- if (nanoseconds % Duration::MILLISECONDS != 0 &&
+- nanoseconds % Duration::MICROSECONDS == 0) {
+- stream << duration.us() << "us";
+- } else {
+- stream << duration.ms() << "ms";
+- }
+- } else if (duration < Minutes(1)) {
+- if (nanoseconds % Duration::SECONDS != 0 &&
+- nanoseconds % Duration::MILLISECONDS == 0) {
+- stream << duration.ms() << "ms";
+- } else {
+- stream << duration.secs() << "secs";
+- }
+- } else if (duration < Hours(1)) {
+- if (nanoseconds % Duration::MINUTES != 0 &&
+- nanoseconds % Duration::SECONDS == 0) {
+- stream << duration.secs() << "secs";
+- } else {
+- stream << duration.mins() << "mins";
+- }
+- } else if (duration < Days(1)) {
+- if (nanoseconds % Duration::HOURS != 0 &&
+- nanoseconds % Duration::MINUTES == 0) {
+- stream << duration.mins() << "mins";
+- } else {
+- stream << duration.hrs() << "hrs";
+- }
+- } else if (duration < Weeks(1)) {
+- if (nanoseconds % Duration::DAYS != 0 &&
+- nanoseconds % Duration::HOURS == 0) {
+- stream << duration.hrs() << "hrs";
+- } else {
+- stream << duration.days() << "days";
+- }
+- } else {
+- if (nanoseconds % Duration::WEEKS != 0 &&
+- nanoseconds % Duration::DAYS == 0) {
+- stream << duration.days() << "days";
+- } else {
+- stream << duration.weeks() << "weeks";
+- }
+- }
+-
+- // Return the stream to original formatting state.
+- stream.precision(precision);
+-
+- return stream;
+-}
+-
+-
+-inline Try<Duration> Duration::create(double seconds)
+-{
+- if (seconds * SECONDS > LLONG_MAX || seconds * SECONDS < LLONG_MIN) {
+- return Error("Argument out of the range that a Duration can represent due "
+- "to int64_t's size limit");
+- }
+-
+- return Nanoseconds(static_cast<int64_t>(seconds * SECONDS));
+-}
+-
+-
+-inline Duration Duration::max() { return Nanoseconds(LLONG_MAX); }
+-
+-
+-inline Duration Duration::min() { return Nanoseconds(LLONG_MIN); }
+-
+-#endif // __STOUT_DURATION_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/error.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/error.hpp
+deleted file mode 100644
+index 97a5cec..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/error.hpp
++++ /dev/null
+@@ -1,72 +0,0 @@
+-#ifndef __STOUT_ERROR_HPP__
+-#define __STOUT_ERROR_HPP__
+-
+-#include <errno.h>
+-#include <string.h> // For strerror.
+-
+-#include <string>
+-
+-#include "result.hpp"
+-#include "try.hpp"
+-
+-// An "error" type that is implicitly convertible to a Try<T> or
+-// Result<T> for any T (effectively "syntactic sugar" to make code
+-// more readable). The implementation uses cast operators to perform
+-// the conversions instead of adding constructors to Try/Result
+-// directly. One could imagine revisiting that decision for C++11
+-// because the use of rvalue reference could eliminate some
+-// unnecessary copies. However, performance is not critical since
+-// Error should not get called very often in practice (if so, it's
+-// probably being used for things that aren't really errors or there
+-// is a more serious problem during execution).
+-
+-class Error
+-{
+-public:
+- explicit Error(const std::string& _message) : message(_message) {}
+-
+- template <typename T>
+- operator Try<T> () const
+- {
+- return Try<T>::error(message);
+- }
+-
+- // Give the compiler some help for nested Try<T>. For example,
+- // enable converting Error to an Option<Try<T>>. Note that this will
+- // bind to the innermost Try<T>.
+- template <template <typename> class S, typename T>
+- operator S<Try<T> > () const
+- {
+- return S<Try<T> >(Try<T>::error(message));
+- }
+-
+- template <typename T>
+- operator Result<T> () const
+- {
+- return Result<T>::error(message);
+- }
+-
+- // Give the compiler some help for nested Result<T>. For example,
+- // enable converting Error to an Option<Result<T>>. Note that this
+- // will bind to the innermost Result<T>.
+- template <template <typename> class S, typename T>
+- operator S<Result<T> > () const
+- {
+- return S<Result<T> >(Result<T>::error(message));
+- }
+-
+- const std::string message;
+-};
+-
+-
+-class ErrnoError : public Error
+-{
+-public:
+- ErrnoError()
+- : Error(std::string(strerror(errno))) {}
+-
+- ErrnoError(const std::string& message)
+- : Error(message + ": " + std::string(strerror(errno))) {}
+-};
+-
+-#endif // __STOUT_ERROR_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/exit.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/exit.hpp
+deleted file mode 100644
+index e8da726..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/exit.hpp
++++ /dev/null
+@@ -1,37 +0,0 @@
+-#ifndef __STOUT_EXIT_HPP__
+-#define __STOUT_EXIT_HPP__
+-
+-#include <stdlib.h>
+-
+-#include <iostream> // For std::cerr.
+-#include <ostream>
+-#include <sstream>
+-#include <string>
+-
+-// Exit takes an exit status and provides a stream for output prior to
+-// exiting. This is like glog's LOG(FATAL) or CHECK, except that it
+-// does _not_ print a stack trace.
+-//
+-// Ex: EXIT(1) << "Cgroups are not present in this system.";
+-#define EXIT(status) __Exit(status).stream()
+-
+-struct __Exit
+-{
+- __Exit(int _status) : status(_status) {}
+-
+- ~__Exit()
+- {
+- std::cerr << out.str() << std::endl;
+- exit(status);
+- }
+-
+- std::ostream& stream()
+- {
+- return out;
+- }
+-
+- std::ostringstream out;
+- const int status;
+-};
+-
+-#endif // __STOUT_EXIT_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/fatal.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/fatal.hpp
+deleted file mode 100644
+index eabee3e..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/fatal.hpp
++++ /dev/null
+@@ -1,43 +0,0 @@
+-#ifndef __STOUT_FATAL_HPP__
+-#define __STOUT_FATAL_HPP__
+-
+-#include <stdarg.h>
+-#include <stdio.h>
+-#include <stdlib.h>
+-
+-
+-/*
+- * Like the non-debug version except includes the file name and line
+- * number in the output.
+- */
+-#define fatal(fmt...) __fatal(__FILE__, __LINE__, fmt)
+-inline void __fatal(const char *file, int line, const char *fmt, ...)
+-{
+- va_list args;
+- va_start(args, fmt);
+- vfprintf(stderr, fmt, args);
+- fprintf(stderr, " (%s:%u)\n", file, line);
+- fflush(stderr);
+- va_end(args);
+- exit(1);
+-}
+-
+-
+-/*
+- * Like the non-debug version except includes the file name and line
+- * number in the output.
+- */
+-#define fatalerror(fmt...) __fatalerror(__FILE__, __LINE__, fmt)
+-inline void __fatalerror(const char *file, int line, const char *fmt, ...)
+-{
+- va_list args;
+- va_start(args, fmt);
+- vfprintf(stderr, fmt, args);
+- fprintf(stderr, " (%s:%u): ", file, line);
+- perror(NULL);
+- fflush(stderr);
+- va_end(args);
+- exit(1);
+-}
+-
+-#endif // __STOUT_FATAL_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/flags.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/flags.hpp
+deleted file mode 100644
+index a70db19..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/flags.hpp
++++ /dev/null
+@@ -1,70 +0,0 @@
+-#ifndef __STOUT_FLAGS_HPP__
+-#define __STOUT_FLAGS_HPP__
+-
+-#include <stout/flags/flags.hpp>
+-
+-// An abstraction for application/library "flags". An example is
+-// probably best:
+-// -------------------------------------------------------------
+-// class MyFlags : public virtual FlagsBase // Use 'virtual' for composition!
+-// {
+-// public:
+-// Flags()
+-// {
+-// add(&debug,
+-// "debug",
+-// "Help string for debug",
+-// false);
+-//
+-// add(&name,
+-// "name",
+-// "Help string for name");
+-// }
+-
+-// bool debug;
+-// Option<string> name;
+-// };
+-//
+-// ...
+-//
+-// map<string, Option<string> > values;
+-// values["no-debug"] = None(); // --no-debug
+-// values["debug"] = None(); // --debug
+-// values["debug"] = Some("true"); // --debug=true
+-// values["debug"] = Some("false"); // --debug=false
+-// values["name"] = Some("frank"); // --name=frank
+-//
+-// MyFlags flags;
+-// flags.load(values);
+-// flags.name.isSome() ...
+-// flags.debug ...
+-// -------------------------------------------------------------
+-//
+-// You can also compose flags provided that each has used "virtual
+-// inheritance":
+-// -------------------------------------------------------------
+-// Flags<MyFlags1, MyFlags2> flags;
+-// flags.add(...); // Any other flags you want to throw in there.
+-// flags.load(values);
+-// flags.flag_from_myflags1 ...
+-// flags.flag_from_myflags2 ...
+-// -------------------------------------------------------------
+-//
+-// "Fail early, fail often":
+-//
+-// You can not add duplicate flags, this is checked for you at compile
+-// time for composite flags (e.g., Flag<MyFlags1, MyFlags2>) and also
+-// checked at runtime for any other flags added via inheritance or
+-// Flags::add(...).
+-//
+-// Flags that can not be loaded (e.g., attempting to use the 'no-'
+-// prefix for a flag that is not boolean) will print a message to
+-// standard error and abort the process.
+-
+-// TODO(benh): Provide a boolean which specifies whether or not to
+-// abort on duplicates or load errors.
+-
+-// TODO(benh): Make prefix for environment variables configurable
+-// (e.g., "MESOS_").
+-
+-#endif // __STOUT_FLAGS_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/flags/flag.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/flags/flag.hpp
+deleted file mode 100644
+index d31c984..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/flags/flag.hpp
++++ /dev/null
+@@ -1,26 +0,0 @@
+-#ifndef __STOUT_FLAGS_FLAG_HPP__
+-#define __STOUT_FLAGS_FLAG_HPP__
+-
+-#include <string>
+-
+-#include <tr1/functional>
+-
+-#include <stout/nothing.hpp>
+-#include <stout/try.hpp>
+-
+-namespace flags {
+-
+-// Forward declaration.
+-class FlagsBase;
+-
+-struct Flag
+-{
+- std::string name;
+- std::string help;
+- bool boolean;
+- std::tr1::function<Try<Nothing>(FlagsBase*, const std::string&)> loader;
+-};
+-
+-} // namespace flags {
+-
+-#endif // __STOUT_FLAGS_FLAG_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/flags/flags.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/flags/flags.hpp
+deleted file mode 100644
+index cfe996e..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/flags/flags.hpp
++++ /dev/null
+@@ -1,488 +0,0 @@
+-#ifndef __STOUT_FLAGS_FLAGS_HPP__
+-#define __STOUT_FLAGS_FLAGS_HPP__
+-
+-#include <stdlib.h> // For abort.
+-
+-#include <map>
+-#include <string>
+-#include <typeinfo> // For typeid.
+-
+-#include <tr1/functional>
+-
+-#include <stout/error.hpp>
+-#include <stout/exit.hpp>
+-#include <stout/foreach.hpp>
+-#include <stout/none.hpp>
+-#include <stout/nothing.hpp>
+-#include <stout/option.hpp>
+-#include <stout/os.hpp>
+-#include <stout/some.hpp>
+-#include <stout/stringify.hpp>
+-#include <stout/strings.hpp>
+-#include <stout/try.hpp>
+-
+-#include <stout/flags/flag.hpp>
+-#include <stout/flags/loader.hpp>
+-#include <stout/flags/parse.hpp>
+-
+-namespace flags {
+-
+-class FlagsBase
+-{
+-public:
+- virtual ~FlagsBase() {}
+-
+- // Load any flags from the environment given the variable prefix,
+- // i.e., given prefix 'STOUT_' will load a flag named 'foo' via
+- // environment variables 'STOUT_foo' or 'STOUT_FOO'.
+- virtual Try<Nothing> load(const std::string& prefix);
+-
+- // Load any flags from the environment given the variable prefix
+- // (see above) followed by loading from the command line (via 'argc'
+- // and 'argv'). If 'unknowns' is true then we'll ignore unknown
+- // flags we see while loading. If 'duplicates' is true then we'll
+- // ignore any duplicates we see while loading.
+- virtual Try<Nothing> load(
+- const Option<std::string>& prefix,
+- int argc,
+- char** argv,
+- bool unknowns = false,
+- bool duplicates = false);
+-
+- Try<Nothing> load(
+- const std::string& prefix,
+- int argc,
+- char** argv,
+- bool unknowns = false,
+- bool duplicates = false);
+-
+- virtual Try<Nothing> load(
+- const std::map<std::string, Option<std::string> >& values,
+- bool unknowns = false);
+-
+- virtual Try<Nothing> load(
+- const std::map<std::string, std::string>& values,
+- bool unknowns = false);
+-
+- // Returns a string describing the flags.
+- std::string usage() const;
+-
+- typedef std::map<std::string, Flag>::const_iterator const_iterator;
+-
+- const_iterator begin() const { return flags.begin(); }
+- const_iterator end() const { return flags.end(); }
+-
+- template <typename T1, typename T2>
+- void add(T1* t1,
+- const std::string& name,
+- const std::string& help,
+- const T2& t2);
+-
+- template <typename T>
+- void add(Option<T>* option,
+- const std::string& name,
+- const std::string& help);
+-
+-protected:
+- template <typename Flags, typename T1, typename T2>
+- void add(T1 Flags::*t1,
+- const std::string& name,
+- const std::string& help,
+- const T2& t2);
+-
+- template <typename Flags, typename T>
+- void add(Option<T> Flags::*option,
+- const std::string& name,
+- const std::string& help);
+-
+- void add(const Flag& flag);
+-
+-private:
+- // Extract environment variable "flags" with the specified prefix.
+- std::map<std::string, Option<std::string> > extract(
+- const std::string& prefix);
+-
+- std::map<std::string, Flag> flags;
+-};
+-
+-
+-// Need to declare/define some explicit subclasses of FlagsBase so
+-// that we can overload the 'Flags::operator FlagsN () const'
+-// functions for each possible type.
+-class _Flags1 : public virtual FlagsBase {};
+-class _Flags2 : public virtual FlagsBase {};
+-class _Flags3 : public virtual FlagsBase {};
+-class _Flags4 : public virtual FlagsBase {};
+-class _Flags5 : public virtual FlagsBase {};
+-
+-
+-// TODO(benh): Add some "type constraints" for template paramters to
+-// make sure they are all of type FlagsBase.
+-template <typename Flags1 = _Flags1,
+- typename Flags2 = _Flags2,
+- typename Flags3 = _Flags3,
+- typename Flags4 = _Flags4,
+- typename Flags5 = _Flags5>
+-class Flags : public virtual Flags1,
+- public virtual Flags2,
+- public virtual Flags3,
+- public virtual Flags4,
+- public virtual Flags5 {};
+-
+-
+-template <typename T1, typename T2>
+-void FlagsBase::add(
+- T1* t1,
+- const std::string& name,
+- const std::string& help,
+- const T2& t2)
+-{
+- *t1 = t2; // Set the default.
+-
+- Flag flag;
+- flag.name = name;
+- flag.help = help;
+- flag.boolean = typeid(T1) == typeid(bool);
+- flag.loader = std::tr1::bind(
+- &Loader<T1>::load,
+- t1,
+- std::tr1::function<Try<T1>(const std::string&)>(
+- std::tr1::bind(&parse<T1>, std::tr1::placeholders::_1)),
+- name,
+- std::tr1::placeholders::_2); // Use _2 because ignore FlagsBase*.
+-
+- // Update the help string to include the default value.
+- flag.help += help.size() > 0 && help.find_last_of("\n\r") != help.size() - 1
+- ? " (default: " // On same line, add space.
+- : "(default: "; // On newline.
+- flag.help += stringify(t2);
+- flag.help += ")";
+-
+- FlagsBase::add(flag);
+-}
+-
+-
+-template <typename T>
+-void FlagsBase::add(
+- Option<T>* option,
+- const std::string& name,
+- const std::string& help)
+-{
+- Flag flag;
+- flag.name = name;
+- flag.help = help;
+- flag.boolean = typeid(T) == typeid(bool);
+- flag.loader = std::tr1::bind(
+- &OptionLoader<T>::load,
+- option,
+- std::tr1::function<Try<T>(const std::string&)>(
+- std::tr1::bind(&parse<T>, std::tr1::placeholders::_1)),
+- name,
+- std::tr1::placeholders::_2); // Use _2 because ignore FlagsBase*.
+-
+- FlagsBase::add(flag);
+-}
+-
+-
+-template <typename Flags, typename T1, typename T2>
+-void FlagsBase::add(
+- T1 Flags::*t1,
+- const std::string& name,
+- const std::string& help,
+- const T2& t2)
+-{
+- Flags* flags = dynamic_cast<Flags*>(this);
+- if (flags == NULL) {
+- std::cerr << "Attempted to add flag '" << name
+- << "' with incompatible type" << std::endl;
+- abort();
+- } else {
+- flags->*t1 = t2; // Set the default.
+- }
+-
+- Flag flag;
+- flag.name = name;
+- flag.help = help;
+- flag.boolean = typeid(T1) == typeid(bool);
+- flag.loader = std::tr1::bind(
+- &MemberLoader<Flags, T1>::load,
+- std::tr1::placeholders::_1,
+- t1,
+- std::tr1::function<Try<T1>(const std::string&)>(
+- std::tr1::bind(&parse<T1>, std::tr1::placeholders::_1)),
+- name,
+- std::tr1::placeholders::_2);
+-
+- // Update the help string to include the default value.
+- flag.help += help.size() > 0 && help.find_last_of("\n\r") != help.size() - 1
+- ? " (default: " // On same line, add space.
+- : "(default: "; // On newline.
+- flag.help += stringify(t2);
+- flag.help += ")";
+-
+- add(flag);
+-}
+-
+-
+-template <typename Flags, typename T>
+-void FlagsBase::add(
+- Option<T> Flags::*option,
+- const std::string& name,
+- const std::string& help)
+-{
+- Flags* flags = dynamic_cast<Flags*>(this);
+- if (flags == NULL) {
+- std::cerr << "Attempted to add flag '" << name
+- << "' with incompatible type" << std::endl;
+- abort();
+- }
+-
+- Flag flag;
+- flag.name = name;
+- flag.help = help;
+- flag.boolean = typeid(T) == typeid(bool);
+- flag.loader = std::tr1::bind(
+- &OptionMemberLoader<Flags, T>::load,
+- std::tr1::placeholders::_1,
+- option,
+- std::tr1::function<Try<T>(const std::string&)>(
+- std::tr1::bind(&parse<T>, std::tr1::placeholders::_1)),
+- name,
+- std::tr1::placeholders::_2);
+-
+- add(flag);
+-}
+-
+-
+-inline void FlagsBase::add(const Flag& flag)
+-{
+- if (flags.count(flag.name) > 0) {
+- EXIT(1) << "Attempted to add duplicate flag '" << flag.name << "'";
+- } else if (flag.name.find("no-") == 0) {
+- EXIT(1) << "Attempted to add flag '" << flag.name
+- << "' that starts with the reserved 'no-' prefix";
+- }
+-
+- flags[flag.name] = flag;
+-}
+-
+-
+-// Extract environment variable "flags" with the specified prefix.
+-inline std::map<std::string, Option<std::string> > FlagsBase::extract(
+- const std::string& prefix)
+-{
+- char** environ = os::environ();
+-
+- std::map<std::string, Option<std::string> > values;
+-
+- for (int i = 0; environ[i] != NULL; i++) {
+- std::string variable = environ[i];
+- if (variable.find(prefix) == 0) {
+- size_t eq = variable.find_first_of("=");
+- if (eq == std::string::npos) {
+- continue; // Not expecting a missing '=', but ignore anyway.
+- }
+-
+- std::string name = variable.substr(prefix.size(), eq - prefix.size());
+- name = strings::lower(name); // Allow PREFIX_NAME or PREFIX_name.
+-
+- // Only add if it's a known flag.
+- if (flags.count(name) > 0 ||
+- (name.find("no-") == 0 && flags.count(name.substr(3)) > 0)) {
+- std::string value = variable.substr(eq + 1);
+- values[name] = Some(value);
+- }
+- }
+- }
+-
+- return values;
+-}
+-
+-
+-inline Try<Nothing> FlagsBase::load(const std::string& prefix)
+-{
+- return load(extract(prefix));
+-}
+-
+-
+-inline Try<Nothing> FlagsBase::load(
+- const Option<std::string>& prefix,
+- int argc,
+- char** argv,
+- bool unknowns,
+- bool duplicates)
+-{
+- std::map<std::string, Option<std::string> > values;
+-
+- if (prefix.isSome()) {
+- values = extract(prefix.get());
+- }
+-
+- // Read flags from the command line.
+- for (int i = 1; i < argc; i++) {
+- const std::string arg(strings::trim(argv[i]));
+-
+- if (arg.find("--") != 0) {
+- continue;
+- }
+-
+- std::string name;
+- Option<std::string> value = None();
+-
+- size_t eq = arg.find_first_of("=");
+- if (eq == std::string::npos && arg.find("--no-") == 0) { // --no-name
+- name = arg.substr(2);
+- } else if (eq == std::string::npos) { // --name
+- name = arg.substr(2);
+- } else { // --name=value
+- name = arg.substr(2, eq - 2);
+- value = arg.substr(eq + 1);
+- }
+-
+- name = strings::lower(name);
+-
+- if (!duplicates) {
+- if (values.count(name) > 0 ||
+- (name.find("no-") == 0 && values.count(name.substr(3)) > 0)) {
+- return Error("Duplicate flag '" + name + "' on command line");
+- }
+- }
+-
+- values[name] = value;
+- }
+-
+- return load(values, unknowns);
+-}
+-
+-
+-inline Try<Nothing> FlagsBase::load(
+- const std::string& prefix,
+- int argc,
+- char** argv,
+- bool unknowns,
+- bool duplicates)
+-{
+- return load(Some(prefix), argc, argv, unknowns, duplicates);
+-}
+-
+-
+-inline Try<Nothing> FlagsBase::load(
+- const std::map<std::string, Option<std::string> >& values,
+- bool unknowns)
+-{
+- std::map<std::string, Option<std::string> >::const_iterator iterator;
+-
+- for (iterator = values.begin(); iterator != values.end(); ++iterator) {
+- const std::string& name = iterator->first;
+- const Option<std::string>& value = iterator->second;
+-
+- if (flags.count(name) > 0) {
+- if (value.isSome()) { // --name=value
+- if (flags[name].boolean && value.get() == "") {
+- flags[name].loader(this, "true"); // Should never fail.
+- } else {
+- Try<Nothing> loader = flags[name].loader(this, value.get());
+- if (loader.isError()) {
+- return Error(
+- "Failed to load flag '" + name + "': " + loader.error());
+- }
+- }
+- } else { // --name
+- if (flags[name].boolean) {
+- flags[name].loader(this, "true"); // Should never fail.
+- } else {
+- return Error(
+- "Failed to load non-boolean flag '" + name + "': Missing value");
+- }
+- }
+- } else if (name.find("no-") == 0) {
+- if (flags.count(name.substr(3)) > 0) { // --no-name
+- if (flags[name.substr(3)].boolean) {
+- if (value.isNone() || value.get() == "") {
+- flags[name.substr(3)].loader(this, "false"); // Should never fail.
+- } else {
+- return Error(
+- "Failed to load boolean flag '" + name.substr(3) +
+- "' via '" + name + "' with value '" + value.get() + "'");
+- }
+- } else {
+- return Error(
+- "Failed to load non-boolean flag '" + name.substr(3) +
+- "' via '" + name + "'");
+- }
+- } else {
+- return Error(
+- "Failed to load unknown flag '" + name.substr(3) +
+- "' via '" + name + "'");
+- }
+- } else if (!unknowns) {
+- return Error("Failed to load unknown flag '" + name + "'");
+- }
+- }
+-
+- return Nothing();
+-}
+-
+-
+-inline Try<Nothing> FlagsBase::load(
+- const std::map<std::string, std::string>& _values,
+- bool unknowns)
+-{
+- std::map<std::string, Option<std::string> > values;
+- std::map<std::string, std::string>::const_iterator iterator;
+- for (iterator = _values.begin(); iterator != _values.end(); ++iterator) {
+- const std::string& name = iterator->first;
+- const std::string& value = iterator->second;
+- values[name] = Some(value);
+- }
+- return load(values, unknowns);
+-}
+-
+-
+-inline std::string FlagsBase::usage() const
+-{
+- const int PAD = 5;
+-
+- std::string usage;
+-
+- std::map<std::string, std::string> col1; // key -> col 1 string
+-
+- // Construct string for the first column and store width of column.
+- size_t width = 0;
+-
+- foreachvalue (const flags::Flag& flag, *this) {
+- if (flag.boolean) {
+- col1[flag.name] = " --[no-]" + flag.name;
+- } else {
+- col1[flag.name] = " --" + flag.name + "=VALUE";
+- }
+- width = std::max(width, col1[flag.name].size());
+- }
+-
+- foreachvalue (const flags::Flag& flag, *this) {
+- std::string line = col1[flag.name];
+-
+- std::string pad(PAD + width - line.size(), ' ');
+- line += pad;
+-
+- size_t pos1 = 0, pos2 = 0;
+- pos2 = flag.help.find_first_of("\n\r", pos1);
+- line += flag.help.substr(pos1, pos2 - pos1) + "\n";
+- usage += line;
+-
+- while (pos2 != std::string::npos) { // Handle multi-line help strings.
+- line = "";
+- pos1 = pos2 + 1;
+- std::string pad2(PAD + width, ' ');
+- line += pad2;
+- pos2 = flag.help.find_first_of("\n\r", pos1);
+- line += flag.help.substr(pos1, pos2 - pos1) + "\n";
+- usage += line;
+- }
+- }
+- return usage;
+-}
+-
+-} // namespace flags {
+-
+-#endif // __STOUT_FLAGS_FLAGS_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/flags/loader.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/flags/loader.hpp
+deleted file mode 100644
+index a6e0f58..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/flags/loader.hpp
++++ /dev/null
+@@ -1,110 +0,0 @@
+-#ifndef __STOUT_FLAGS_LOADER_HPP__
+-#define __STOUT_FLAGS_LOADER_HPP__
+-
+-#include <string>
+-
+-#include <tr1/functional>
+-
+-#include <stout/error.hpp>
+-#include <stout/nothing.hpp>
+-#include <stout/option.hpp>
+-#include <stout/some.hpp>
+-#include <stout/try.hpp>
+-
+-#include <stout/flags/parse.hpp>
+-
+-namespace flags {
+-
+-// Forward declaration.
+-class FlagsBase;
+-
+-template <typename T>
+-struct Loader
+-{
+- static Try<Nothing> load(
+- T* flag,
+- const std::tr1::function<Try<T>(const std::string&)>& parse,
+- const std::string& name,
+- const std::string& value)
+- {
+- Try<T> t = parse(value);
+- if (t.isSome()) {
+- *flag = t.get();
+- } else {
+- return Error("Failed to load value '" + value + "': " + t.error());
+- }
+- return Nothing();
+- }
+-};
+-
+-
+-template <typename T>
+-struct OptionLoader
+-{
+- static Try<Nothing> load(
+- Option<T>* flag,
+- const std::tr1::function<Try<T>(const std::string&)>& parse,
+- const std::string& name,
+- const std::string& value)
+- {
+- Try<T> t = parse(value);
+- if (t.isSome()) {
+- *flag = Some(t.get());
+- } else {
+- return Error("Failed to load value '" + value + "': " + t.error());
+- }
+- return Nothing();
+- }
+-};
+-
+-
+-template <typename F, typename T>
+-struct MemberLoader
+-{
+- static Try<Nothing> load(
+- FlagsBase* base,
+- T F::*flag,
+- const std::tr1::function<Try<T>(const std::string&)>& parse,
+- const std::string& name,
+- const std::string& value)
+- {
+- F* f = dynamic_cast<F*>(base);
+- if (f != NULL) {
+- Try<T> t = parse(value);
+- if (t.isSome()) {
+- f->*flag = t.get();
+- } else {
+- return Error("Failed to load value '" + value + "': " + t.error());
+- }
+- }
+- return Nothing();
+- }
+-};
+-
+-
+-template <typename F, typename T>
+-struct OptionMemberLoader
+-{
+- static Try<Nothing> load(
+- FlagsBase* base,
+- Option<T> F::*flag,
+- const std::tr1::function<Try<T>(const std::string&)>& parse,
+- const std::string& name,
+- const std::string& value)
+- {
+- F* f = dynamic_cast<F*>(base);
+- if (f != NULL) {
+- Try<T> t = parse(value);
+- if (t.isSome()) {
+- f->*flag = Some(t.get());
+- } else {
+- return Error("Failed to load value '" + value + "': " + t.error());
+- }
+- }
+- return Nothing();
+- }
+-};
+-
+-} // namespace flags {
+-
+-#endif // __STOUT_FLAGS_LOADER_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/flags/parse.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/flags/parse.hpp
+deleted file mode 100644
+index 4c5b297..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/flags/parse.hpp
++++ /dev/null
+@@ -1,62 +0,0 @@
+-#ifndef __STOUT_FLAGS_PARSE_HPP__
+-#define __STOUT_FLAGS_PARSE_HPP__
+-
+-#include <sstream> // For istringstream.
+-#include <string>
+-
+-#include <tr1/functional>
+-
+-#include <stout/duration.hpp>
+-#include <stout/error.hpp>
+-#include <stout/try.hpp>
+-
+-namespace flags {
+-
+-template <typename T>
+-Try<T> parse(const std::string& value)
+-{
+- T t;
+- std::istringstream in(value);
+- in >> t;
+- if (!in.good() && !in.eof()) {
+- return Error("Failed to convert into required type");
+- }
+- return t;
+-}
+-
+-
+-template <>
+-inline Try<std::string> parse(const std::string& value)
+-{
+- return value;
+-}
+-
+-
+-template <>
+-inline Try<bool> parse(const std::string& value)
+-{
+- if (value == "true" || value == "1") {
+- return true;
+- } else if (value == "false" || value == "0") {
+- return false;
+- }
+- return Error("Expecting a boolean (e.g., true or false)");
+-}
+-
+-
+-template <>
+-inline Try<Duration> parse(const std::string& value)
+-{
+- return Duration::parse(value);
+-}
+-
+-
+-template <>
+-inline Try<Bytes> parse(const std::string& value)
+-{
+- return Bytes::parse(value);
+-}
+-
+-} // namespace flags {
+-
+-#endif // __STOUT_FLAGS_PARSE_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/foreach.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/foreach.hpp
+deleted file mode 100644
+index 0afe285..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/foreach.hpp
++++ /dev/null
+@@ -1,51 +0,0 @@
+-#ifndef __STOUT_FOREACH_HPP__
+-#define __STOUT_FOREACH_HPP__
+-
+-#include <boost/foreach.hpp>
+-
+-#include <boost/tuple/tuple.hpp>
+-
+-namespace __foreach__ {
+-
+-// NOTE: This is a copied from Boost
+-// (boost/tuple/detail/tuple_basic_no_partial_spec.hpp) because the
+-// new 'boost::tuples::ignore' does not work in our 'foreachkey' and
+-// 'foreachvalue'.
+-struct swallow_assign {
+- template<typename T>
+- swallow_assign const& operator=(const T&) const {
+- return *this;
+- }
+-};
+-
+-swallow_assign const ignore = swallow_assign();
+-
+-} // namespace __foreach__ {
+-
+-#define BOOST_FOREACH_PAIR(VARFIRST, VARSECOND, COL) \
+- BOOST_FOREACH_PREAMBLE() \
+- if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_col) = BOOST_FOREACH_CONTAIN(COL)) {} else \
+- if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_cur) = BOOST_FOREACH_BEGIN(COL)) {} else \
+- if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_end) = BOOST_FOREACH_END(COL)) {} else \
+- for (bool BOOST_FOREACH_ID(_foreach_continue) = true, BOOST_FOREACH_ID(_foreach_onetime) = true; \
+- BOOST_FOREACH_ID(_foreach_continue) && !BOOST_FOREACH_DONE(COL); \
+- BOOST_FOREACH_ID(_foreach_continue) ? BOOST_FOREACH_NEXT(COL) : (void)0) \
+- if (boost::foreach_detail_::set_false(BOOST_FOREACH_ID(_foreach_onetime))) {} else \
+- for (VARFIRST = BOOST_FOREACH_DEREF(COL).first; \
+- !BOOST_FOREACH_ID(_foreach_onetime); \
+- BOOST_FOREACH_ID(_foreach_onetime) = true) \
+- if (boost::foreach_detail_::set_false(BOOST_FOREACH_ID(_foreach_continue))) {} else \
+- for (VARSECOND = BOOST_FOREACH_DEREF(COL).second; \
+- !BOOST_FOREACH_ID(_foreach_continue); \
+- BOOST_FOREACH_ID(_foreach_continue) = true)
+-
+-#define foreach BOOST_FOREACH
+-#define foreachpair BOOST_FOREACH_PAIR
+-
+-#define foreachkey(VAR, COL) \
+- foreachpair (VAR, __foreach__::ignore, COL)
+-
+-#define foreachvalue(VAR, COL) \
+- foreachpair (__foreach__::ignore, VAR, COL)
+-
+-#endif // __STOUT_FOREACH_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/format.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/format.hpp
+deleted file mode 100644
+index cae7fcb..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/format.hpp
++++ /dev/null
+@@ -1,382 +0,0 @@
+-#ifndef __STOUT_FORMAT_HPP__
+-#define __STOUT_FORMAT_HPP__
+-
+-#include <stdarg.h> // For 'va_list', 'va_start', 'va_end'.
+-#include <stdio.h> // For 'vasprintf'.
+-
+-#include <string>
+-
+-#include <tr1/type_traits> // For 'is_pod'.
+-
+-#include "error.hpp"
+-#include "try.hpp"
+-#include "stringify.hpp"
+-
+-
+-// The 'strings::format' functions produces strings based on the
+-// printf family of functions. Except, unlike the printf family of
+-// functions, the 'strings::format' routines attempt to "stringify"
+-// any arguments that are not POD types (i.e., "plain old data":
+-// primitives, pointers, certain structs/classes and unions,
+-// etc). This enables passing structs/classes to 'strings::format'
+-// provided there is a definition/specialization of 'ostream::operator
+-// <<' available for that type. Note that the '%s' format specifier is
+-// expected for each argument that gets stringified. A specialization
+-// for std::string is also provided so that std::string::c_str is not
+-// necessary (but again, '%s' is expected as the format specifier).
+-
+-namespace strings {
+-namespace internal {
+-
+-Try<std::string> format(const std::string& fmt, va_list args);
+-Try<std::string> format(const std::string& fmt, ...);
+-
+-template <typename T, bool b>
+-struct stringify;
+-
+-} // namespace internal {
+-
+-
+-#if __cplusplus >= 201103L
+-template <typename ...T>
+-Try<std::string> format(const std::string& s, const T& ...t)
+-{
+- return internal::format(
+- s,
+- internal::stringify<T, !std::is_pod<T>::value>(t).get()...);
+-}
+-#else
+-template <typename T1>
+-Try<std::string> format(const std::string& s,
+- const T1& t1)
+-{
+- return internal::format(
+- s,
+- internal::stringify<T1, !std::tr1::is_pod<T1>::value>(t1).get());
+-}
+-
+-
+-template <typename T1,
+- typename T2>
+-Try<std::string> format(const std::string& s,
+- const T1& t1,
+- const T2& t2)
+-{
+- return internal::format(
+- s,
+- internal::stringify<T1, !std::tr1::is_pod<T1>::value>(t1).get(),
+- internal::stringify<T2, !std::tr1::is_pod<T1>::value>(t2).get());
+-}
+-
+-
+-template <typename T1,
+- typename T2,
+- typename T3>
+-Try<std::string> format(const std::string& s,
+- const T1& t1,
+- const T2& t2,
+- const T3& t3)
+-{
+- return internal::format(
+- s,
+- internal::stringify<T1, !std::tr1::is_pod<T1>::value>(t1).get(),
+- internal::stringify<T2, !std::tr1::is_pod<T2>::value>(t2).get(),
+- internal::stringify<T3, !std::tr1::is_pod<T3>::value>(t3).get());
+-}
+-
+-
+-template <typename T1,
+- typename T2,
+- typename T3,
+- typename T4>
+-Try<std::string> format(const std::string& s,
+- const T1& t1,
+- const T2& t2,
+- const T3& t3,
+- const T4& t4)
+-{
+- return internal::format(
+- s,
+- internal::stringify<T1, !std::tr1::is_pod<T1>::value>(t1).get(),
+- internal::stringify<T2, !std::tr1::is_pod<T2>::value>(t2).get(),
+- internal::stringify<T3, !std::tr1::is_pod<T3>::value>(t3).get(),
+- internal::stringify<T4, !std::tr1::is_pod<T4>::value>(t4).get());
+-}
+-
+-
+-template <typename T1,
+- typename T2,
+- typename T3,
+- typename T4,
+- typename T5>
+-Try<std::string> format(const std::string& s,
+- const T1& t1,
+- const T2& t2,
+- const T3& t3,
+- const T4& t4,
+- const T5& t5)
+-{
+- return internal::format(
+- s,
+- internal::stringify<T1, !std::tr1::is_pod<T1>::value>(t1).get(),
+- internal::stringify<T2, !std::tr1::is_pod<T2>::value>(t2).get(),
+- internal::stringify<T3, !std::tr1::is_pod<T3>::value>(t3).get(),
+- internal::stringify<T4, !std::tr1::is_pod<T4>::value>(t4).get(),
+- internal::stringify<T5, !std::tr1::is_pod<T5>::value>(t5).get());
+-}
+-
+-
+-template <typename T1,
+- typename T2,
+- typename T3,
+- typename T4,
+- typename T5,
+- typename T6>
+-Try<std::string> format(const std::string& s,
+- const T1& t1,
+- const T2& t2,
+- const T3& t3,
+- const T4& t4,
+- const T5& t5,
+- const T6& t6)
+-{
+- return internal::format(
+- s,
+- internal::stringify<T1, !std::tr1::is_pod<T1>::value>(t1).get(),
+- internal::stringify<T2, !std::tr1::is_pod<T2>::value>(t2).get(),
+- internal::stringify<T3, !std::tr1::is_pod<T3>::value>(t3).get(),
+- internal::stringify<T4, !std::tr1::is_pod<T4>::value>(t4).get(),
+- internal::stringify<T5, !std::tr1::is_pod<T5>::value>(t5).get(),
+- internal::stringify<T6, !std::tr1::is_pod<T6>::value>(t6).get());
+-}
+-
+-
+-template <typename T1,
+- typename T2,
+- typename T3,
+- typename T4,
+- typename T5,
+- typename T6,
+- typename T7>
+-Try<std::string> format(const std::string& s,
+- const T1& t1,
+- const T2& t2,
+- const T3& t3,
+- const T4& t4,
+- const T5& t5,
+- const T6& t6,
+- const T7& t7)
+-{
+- return internal::format(
+- s,
+- internal::stringify<T1, !std::tr1::is_pod<T1>::value>(t1).get(),
+- internal::stringify<T2, !std::tr1::is_pod<T2>::value>(t2).get(),
+- internal::stringify<T3, !std::tr1::is_pod<T3>::value>(t3).get(),
+- internal::stringify<T4, !std::tr1::is_pod<T4>::value>(t4).get(),
+- internal::stringify<T5, !std::tr1::is_pod<T5>::value>(t5).get(),
+- internal::stringify<T6, !std::tr1::is_pod<T6>::value>(t6).get(),
+- internal::stringify<T7, !std::tr1::is_pod<T7>::value>(t7).get());
+-}
+-
+-
+-template <typename T1,
+- typename T2,
+- typename T3,
+- typename T4,
+- typename T5,
+- typename T6,
+- typename T7,
+- typename T8>
+-Try<std::string> format(const std::string& s,
+- const T1& t1,
+- const T2& t2,
+- const T3& t3,
+- const T4& t4,
+- const T5& t5,
+- const T6& t6,
+- const T7& t7,
+- const T8& t8)
+-{
+- return internal::format(
+- s,
+- internal::stringify<T1, !std::tr1::is_pod<T1>::value>(t1).get(),
+- internal::stringify<T2, !std::tr1::is_pod<T2>::value>(t2).get(),
+- internal::stringify<T3, !std::tr1::is_pod<T3>::value>(t3).get(),
+- internal::stringify<T4, !std::tr1::is_pod<T4>::value>(t4).get(),
+- internal::stringify<T5, !std::tr1::is_pod<T5>::value>(t5).get(),
+- internal::stringify<T6, !std::tr1::is_pod<T6>::value>(t6).get(),
+- internal::stringify<T7, !std::tr1::is_pod<T7>::value>(t7).get(),
+- internal::stringify<T8, !std::tr1::is_pod<T8>::value>(t8).get());
+-}
+-
+-
+-template <typename T1,
+- typename T2,
+- typename T3,
+- typename T4,
+- typename T5,
+- typename T6,
+- typename T7,
+- typename T8,
+- typename T9>
+-Try<std::string> format(const std::string& s,
+- const T1& t1,
+- const T2& t2,
+- const T3& t3,
+- const T4& t4,
+- const T5& t5,
+- const T6& t6,
+- const T7& t7,
+- const T8& t8,
+- const T9& t9)
+-{
+- return internal::format(
+- s,
+- internal::stringify<T1, !std::tr1::is_pod<T1>::value>(t1).get(),
+- internal::stringify<T2, !std::tr1::is_pod<T2>::value>(t2).get(),
+- internal::stringify<T3, !std::tr1::is_pod<T3>::value>(t3).get(),
+- internal::stringify<T4, !std::tr1::is_pod<T4>::value>(t4).get(),
+- internal::stringify<T5, !std::tr1::is_pod<T5>::value>(t5).get(),
+- internal::stringify<T6, !std::tr1::is_pod<T6>::value>(t6).get(),
+- internal::stringify<T7, !std::tr1::is_pod<T7>::value>(t7).get(),
+- internal::stringify<T8, !std::tr1::is_pod<T8>::value>(t8).get(),
+- internal::stringify<T9, !std::tr1::is_pod<T9>::value>(t9).get());
+-}
+-
+-
+-template <typename T1,
+- typename T2,
+- typename T3,
+- typename T4,
+- typename T5,
+- typename T6,
+- typename T7,
+- typename T8,
+- typename T9,
+- typename T10>
+-Try<std::string> format(const std::string& s,
+- const T1& t1,
+- const T2& t2,
+- const T3& t3,
+- const T4& t4,
+- const T5& t5,
+- const T6& t6,
+- const T7& t7,
+- const T8& t8,
+- const T9& t9,
+- const T10& t10)
+-{
+- return internal::format(
+- s,
+- internal::stringify<T1, !std::tr1::is_pod<T1>::value>(t1).get(),
+- internal::stringify<T2, !std::tr1::is_pod<T2>::value>(t2).get(),
+- internal::stringify<T3, !std::tr1::is_pod<T3>::value>(t3).get(),
+- internal::stringify<T4, !std::tr1::is_pod<T4>::value>(t4).get(),
+- internal::stringify<T5, !std::tr1::is_pod<T5>::value>(t5).get(),
+- internal::stringify<T6, !std::tr1::is_pod<T6>::value>(t6).get(),
+- internal::stringify<T7, !std::tr1::is_pod<T7>::value>(t7).get(),
+- internal::stringify<T8, !std::tr1::is_pod<T8>::value>(t8).get(),
+- internal::stringify<T9, !std::tr1::is_pod<T9>::value>(t9).get(),
+- internal::stringify<T10, !std::tr1::is_pod<T10>::value>(t10).get());
+-}
+-
+-template <typename T1,
+- typename T2,
+- typename T3,
+- typename T4,
+- typename T5,
+- typename T6,
+- typename T7,
+- typename T8,
+- typename T9,
+- typename T10,
+- typename T11>
+-Try<std::string> format(const std::string& s,
+- const T1& t1,
+- const T2& t2,
+- const T3& t3,
+- const T4& t4,
+- const T5& t5,
+- const T6& t6,
+- const T7& t7,
+- const T8& t8,
+- const T9& t9,
+- const T10& t10,
+- const T11& t11)
+-{
+- return internal::format(
+- s,
+- internal::stringify<T1, !std::tr1::is_pod<T1>::value>(t1).get(),
+- internal::stringify<T2, !std::tr1::is_pod<T2>::value>(t2).get(),
+- internal::stringify<T3, !std::tr1::is_pod<T3>::value>(t3).get(),
+- internal::stringify<T4, !std::tr1::is_pod<T4>::value>(t4).get(),
+- internal::stringify<T5, !std::tr1::is_pod<T5>::value>(t5).get(),
+- internal::stringify<T6, !std::tr1::is_pod<T6>::value>(t6).get(),
+- internal::stringify<T7, !std::tr1::is_pod<T7>::value>(t7).get(),
+- internal::stringify<T8, !std::tr1::is_pod<T8>::value>(t8).get(),
+- internal::stringify<T9, !std::tr1::is_pod<T9>::value>(t9).get(),
+- internal::stringify<T10, !std::tr1::is_pod<T10>::value>(t10).get(),
+- internal::stringify<T11, !std::tr1::is_pod<T11>::value>(t11).get());
+-}
+-#endif // __cplusplus >= 201103L
+-
+-
+-namespace internal {
+-
+-inline Try<std::string> format(const std::string& fmt, va_list args)
+-{
+- char* temp;
+- if (vasprintf(&temp, fmt.c_str(), args) == -1) {
+- // Note that temp is undefined, so we do not need to call free.
+- return Error("Failed to format '" + fmt + "' (possibly out of memory)");
+- }
+- std::string result(temp);
+- free(temp);
+- return result;
+-}
+-
+-
+-inline Try<std::string> format(const std::string& fmt, ...)
+-{
+- va_list args;
+- va_start(args, fmt);
+- const Try<std::string>& result = format(fmt, args);
+- va_end(args);
+- return result;
+-}
+-
+-
+-template <typename T>
+-struct stringify<T, false>
+-{
+- stringify(const T& _t) : t(_t) {}
+- const T& get() { return t; }
+- const T& t;
+-};
+-
+-
+-template <typename T>
+-struct stringify<T, true>
+-{
+- stringify(const T& _t) : s(::stringify(_t)) {}
+- const char* get() { return s.c_str(); }
+-
+- // NOTE: We need to do the copy here, because the temporary returned by
+- // ::stringify() doesn't outlive the get() call inside strings::format().
+- // TODO(vinod): Figure out a fix for using const ref here.
+- const std::string s;
+-};
+-
+-
+-template <>
+-struct stringify<std::string, true>
+-{
+- stringify(const std::string& _s) : s(_s) {}
+- const char* get() { return s.c_str(); }
+- const std::string& s;
+-};
+-
+-} // namespace internal {
+-} // namespace strings {
+-
+-#endif // __STOUT_FORMAT_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/fs.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/fs.hpp
+deleted file mode 100644
+index 3a20e86..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/fs.hpp
++++ /dev/null
+@@ -1,54 +0,0 @@
+-#ifndef __STOUT_FS_HPP__
+-#define __STOUT_FS_HPP__
+-
+-#include <unistd.h> // For symlink.
+-
+-#include <sys/statvfs.h>
+-
+-#include <string>
+-
+-#include "bytes.hpp"
+-#include "error.hpp"
+-#include "nothing.hpp"
+-#include "try.hpp"
+-
+-// TODO(bmahler): Merge available() and usage() into df() that returns
+-// a struct, and move this back into os.hpp.
+-namespace fs {
+-
+-// Returns the total disk size in bytes.
+-inline Try<Bytes> size(const std::string& path = "/")
+-{
+- struct statvfs buf;
+- if (::statvfs(path.c_str(), &buf) < 0) {
+- return ErrnoError();
+- }
+- return Bytes(buf.f_blocks * buf.f_frsize);
+-}
+-
+-
+-// Returns relative disk usage of the file system that the given path
+-// is mounted at.
+-inline Try<double> usage(const std::string& path = "/")
+-{
+- struct statvfs buf;
+- if (statvfs(path.c_str(), &buf) < 0) {
+- return ErrnoError("Error invoking statvfs on '" + path + "'");
+- }
+- return (double) (buf.f_blocks - buf.f_bfree) / buf.f_blocks;
+-}
+-
+-
+-inline Try<Nothing> symlink(
+- const std::string& original,
+- const std::string& link)
+-{
+- if (::symlink(original.c_str(), link.c_str()) < 0) {
+- return ErrnoError();
+- }
+- return Nothing();
+-}
+-
+-} // namespace fs {
+-
+-#endif // __STOUT_FS_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/gtest.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/gtest.hpp
+deleted file mode 100644
+index 1f10834..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/gtest.hpp
++++ /dev/null
+@@ -1,130 +0,0 @@
+-#ifndef __STOUT_GTEST_HPP__
+-#define __STOUT_GTEST_HPP__
+-
+-#include <gtest/gtest.h>
+-
+-#include <string>
+-
+-#include <stout/option.hpp>
+-#include <stout/result.hpp>
+-#include <stout/try.hpp>
+-
+-
+-template <typename T>
+-::testing::AssertionResult AssertSome(
+- const char* expr,
+- const Option<T>& actual)
+-{
+- if (actual.isNone()) {
+- return ::testing::AssertionFailure()
+- << expr << " is NONE";
+- }
+-
+- return ::testing::AssertionSuccess();
+-}
+-
+-
+-template <typename T>
+-::testing::AssertionResult AssertSome(
+- const char* expr,
+- const Try<T>& actual)
+-{
+- if (actual.isError()) {
+- return ::testing::AssertionFailure()
+- << expr << ": " << actual.error();
+- }
+-
+- return ::testing::AssertionSuccess();
+-}
+-
+-
+-template <typename T>
+-::testing::AssertionResult AssertSome(
+- const char* expr,
+- const Result<T>& actual)
+-{
+- if (actual.isNone()) {
+- return ::testing::AssertionFailure()
+- << expr << " is NONE";
+- } else if (actual.isError()) {
+- return ::testing::AssertionFailure()
+- << expr << ": " << actual.error();
+- }
+-
+- return ::testing::AssertionSuccess();
+-}
+-
+-
+-template <typename T1, typename T2>
+-::testing::AssertionResult AssertSomeEq(
+- const char* expectedExpr,
+- const char* actualExpr,
+- const T1& expected,
+- const T2& actual) // Duck typing!
+-{
+- const ::testing::AssertionResult result = AssertSome(actualExpr, actual);
+-
+- if (result) {
+- if (expected == actual.get()) {
+- return ::testing::AssertionSuccess();
+- } else {
+- return ::testing::AssertionFailure()
+- << "Value of: (" << actualExpr << ").get()\n"
+- << " Actual: " << ::testing::PrintToString(actual.get()) << "\n"
+- << "Expected: " << expectedExpr << "\n"
+- << "Which is: " << ::testing::PrintToString(expected);
+- }
+- }
+-
+- return result;
+-}
+-
+-
+-#define ASSERT_SOME(actual) \
+- ASSERT_PRED_FORMAT1(AssertSome, actual)
+-
+-
+-#define EXPECT_SOME(actual) \
+- EXPECT_PRED_FORMAT1(AssertSome, actual)
+-
+-
+-#define ASSERT_SOME_EQ(expected, actual) \
+- ASSERT_PRED_FORMAT2(AssertSomeEq, expected, actual)
+-
+-
+-#define EXPECT_SOME_EQ(expected, actual) \
+- EXPECT_PRED_FORMAT2(AssertSomeEq, expected, actual)
+-
+-
+-#define ASSERT_SOME_TRUE(actual) \
+- ASSERT_PRED_FORMAT2(AssertSomeEq, true, actual)
+-
+-
+-#define EXPECT_SOME_TRUE(actual) \
+- EXPECT_PRED_FORMAT2(AssertSomeEq, true, actual)
+-
+-
+-#define ASSERT_SOME_FALSE(actual) \
+- ASSERT_PRED_FORMAT2(AssertSomeEq, false, actual)
+-
+-
+-#define EXPECT_SOME_FALSE(actual) \
+- EXPECT_PRED_FORMAT2(AssertSomeEq, false, actual)
+-
+-
+-#define ASSERT_ERROR(actual) \
+- ASSERT_TRUE(actual.isError())
+-
+-
+-#define EXPECT_ERROR(actual) \
+- EXPECT_TRUE(actual.isError())
+-
+-
+-#define ASSERT_NONE(actual) \
+- ASSERT_TRUE(actual.isNone())
+-
+-
+-#define EXPECT_NONE(actual) \
+- EXPECT_TRUE(actual.isNone())
+-
+-#endif // __STOUT_GTEST_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/gzip.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/gzip.hpp
+deleted file mode 100644
+index bc0d818..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/gzip.hpp
++++ /dev/null
+@@ -1,135 +0,0 @@
+-#ifndef __STOUT_GZIP_HPP__
+-#define __STOUT_GZIP_HPP__
+-
+-#include <zlib.h>
+-
+-#include <string>
+-
+-#include "error.hpp"
+-#include "try.hpp"
+-
+-// Compression utilities.
+-// TODO(bmahler): Provide streaming compression / decompression as well.
+-namespace gzip {
+-
+-// We use a 16KB buffer with zlib compression / decompression.
+-#define GZIP_BUFFER_SIZE 16384
+-
+-// Returns a gzip compressed version of the provided string.
+-// The compression level should be within the range [-1, 9].
+-// See zlib.h:
+-// #define Z_NO_COMPRESSION 0
+-// #define Z_BEST_SPEED 1
+-// #define Z_BEST_COMPRESSION 9
+-// #define Z_DEFAULT_COMPRESSION (-1)
+-inline Try<std::string> compress(
+- const std::string& decompressed,
+- int level = Z_DEFAULT_COMPRESSION)
+-{
+- // Verify the level is within range.
+- if (!(level == Z_DEFAULT_COMPRESSION ||
+- (level >= Z_NO_COMPRESSION && level <= Z_BEST_COMPRESSION))) {
+- return Error("Invalid compression level: " + level);
+- }
+-
+- z_stream_s stream;
+- stream.next_in =
+- const_cast<Bytef*>(reinterpret_cast<const Bytef*>(decompressed.data()));
+- stream.avail_in = decompressed.length();
+- stream.zalloc = Z_NULL;
+- stream.zfree = Z_NULL;
+- stream.opaque = Z_NULL;
+-
+- int code = deflateInit2(
+- &stream,
+- level, // Compression level.
+- Z_DEFLATED, // Compression method.
+- MAX_WBITS + 16, // Zlib magic for gzip compression / decompression.
+- 8, // Default memLevel value.
+- Z_DEFAULT_STRATEGY);
+-
+- if (code != Z_OK) {
+- return Error("Failed to initialize zlib: " + std::string(stream.msg));
+- }
+-
+- // Build up the compressed result.
+- Bytef buffer[GZIP_BUFFER_SIZE];
+- std::string result = "";
+- do {
+- stream.next_out = buffer;
+- stream.avail_out = GZIP_BUFFER_SIZE;
+- code = deflate(&stream, stream.avail_in > 0 ? Z_NO_FLUSH : Z_FINISH);
+-
+- if (code != Z_OK && code != Z_STREAM_END) {
+- Error error(std::string(stream.msg));
+- deflateEnd(&stream);
+- return error;
+- }
+-
+- // Consume output and reset the buffer.
+- result.append(
+- reinterpret_cast<char*>(buffer),
+- GZIP_BUFFER_SIZE - stream.avail_out);
+- stream.next_out = buffer;
+- stream.avail_out = GZIP_BUFFER_SIZE;
+- } while (code != Z_STREAM_END);
+-
+- code = deflateEnd(&stream);
+- if (code != Z_OK) {
+- return Error("Failed to clean up zlib: " + std::string(stream.msg));
+- }
+- return result;
+-}
+-
+-
+-// Returns a gzip decompressed version of the provided string.
+-inline Try<std::string> decompress(const std::string& compressed)
+-{
+- z_stream_s stream;
+- stream.next_in =
+- const_cast<Bytef*>(reinterpret_cast<const Bytef*>(compressed.data()));
+- stream.avail_in = compressed.length();
+- stream.zalloc = Z_NULL;
+- stream.zfree = Z_NULL;
+- stream.opaque = Z_NULL;
+-
+- int code = inflateInit2(
+- &stream,
+- MAX_WBITS + 16); // Zlib magic for gzip compression / decompression.
+-
+- if (code != Z_OK) {
+- return Error("Failed to initialize zlib: " + std::string(stream.msg));
+- }
+-
+- // Build up the decompressed result.
+- Bytef buffer[GZIP_BUFFER_SIZE];
+- std::string result = "";
+- do {
+- stream.next_out = buffer;
+- stream.avail_out = GZIP_BUFFER_SIZE;
+- code = inflate(&stream, stream.avail_in > 0 ? Z_NO_FLUSH : Z_FINISH);
+-
+- if (code != Z_OK && code != Z_STREAM_END) {
+- Error error(std::string(stream.msg));
+- inflateEnd(&stream);
+- return error;
+- }
+-
+- // Consume output and reset the buffer.
+- result.append(
+- reinterpret_cast<char*>(buffer),
+- GZIP_BUFFER_SIZE - stream.avail_out);
+- stream.next_out = buffer;
+- stream.avail_out = GZIP_BUFFER_SIZE;
+- } while (code != Z_STREAM_END);
+-
+- code = inflateEnd(&stream);
+- if (code != Z_OK) {
+- return Error("Failed to clean up zlib: " + std::string(stream.msg));
+- }
+- return result;
+-}
+-
+-} // namespace gzip {
+-
+-#endif // __STOUT_GZIP_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/hashmap.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/hashmap.hpp
+deleted file mode 100644
+index dc78e28..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/hashmap.hpp
++++ /dev/null
+@@ -1,113 +0,0 @@
+-#ifndef __STOUT_HASHMAP_HPP__
+-#define __STOUT_HASHMAP_HPP__
+-
+-#include <utility>
+-
+-#include <boost/get_pointer.hpp>
+-#include <boost/unordered_map.hpp>
+-
+-#include "hashset.hpp"
+-#include "foreach.hpp"
+-#include "none.hpp"
+-#include "option.hpp"
+-
+-
+-// Provides a hash map via Boost's 'unordered_map'. For most intensive
+-// purposes this could be accomplished with a templated typedef, but
+-// those don't exist (until C++-11). Also, doing it this way allows us
+-// to add functionality, or better naming of existing functionality,
+-// etc.
+-
+-template <typename Key, typename Value>
+-class hashmap : public boost::unordered_map<Key, Value>
+-{
+-public:
+- // An explicit default constructor is needed so
+- // 'const hashmap<T> map;' is not an error.
+- hashmap() {}
+-
+- // Checks whether this map contains a binding for a key.
+- bool contains(const Key& key) const
+- {
+- return boost::unordered_map<Key, Value>::count(key) > 0;
+- }
+-
+- // Checks whether there exists a bound value in this map.
+- bool containsValue(const Value& v) const
+- {
+- foreachvalue (const Value& value, *this) {
+- if (value == v) {
+- return true;
+- }
+- }
+- }
+-
+- // Inserts a key, value pair into the map replacing an old value
+- // if the key is already present.
+- void put(const Key& key, const Value& value)
+- {
+- boost::unordered_map<Key, Value>::erase(key);
+- boost::unordered_map<Key, Value>::insert(std::pair<Key, Value>(key, value));
+- }
+-
+- // Returns an Option for the binding to the key.
+- Option<Value> get(const Key& key) const
+- {
+- typedef typename boost::unordered_map<Key, Value>::const_iterator
+- const_iterator;
+- const_iterator it = boost::unordered_map<Key, Value>::find(key);
+- if (it == boost::unordered_map<Key, Value>::end()) {
+- return None();
+- }
+- return it->second;
+- }
+-
+- // Returns the set of keys in this map.
+- // TODO(vinod/bmahler): Should return a list instead.
+- hashset<Key> keys() const
+- {
+- hashset<Key> result;
+- foreachkey (const Key& key, *this) {
+- result.insert(key);
+- }
+- return result;
+- }
+-
+- // Returns the list of values in this map.
+- std::list<Value> values() const
+- {
+- std::list<Value> result;
+- foreachvalue (const Value& value, *this) {
+- result.push_back(value);
+- }
+- return result;
+- }
+-
+- // Checks whether there exists a value in this map that returns the
+- // a result equal to 'r' when the specified method is invoked.
+- template <typename R, typename T>
+- bool existsValue(R (T::*method)(), R r) const
+- {
+- foreachvalue (const Value& value, *this) {
+- const T* t = boost::get_pointer(value);
+- if (t->*method() == r) {
+- return true;
+- }
+- }
+- }
+-
+- // Checks whether there exists a value in this map whose specified
+- // member is equal to 'r'.
+- template <typename R, typename T>
+- bool existsValue(R (T::*member), R r) const
+- {
+- foreachvalue (const Value& value, *this) {
+- const T* t = boost::get_pointer(value);
+- if (t->*member == r) {
+- return true;
+- }
+- }
+- }
+-};
+-
+-#endif // __STOUT_HASHMAP_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/hashset.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/hashset.hpp
+deleted file mode 100644
+index f1f2099..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/hashset.hpp
++++ /dev/null
+@@ -1,69 +0,0 @@
+-#ifndef __STOUT_HASHSET_HPP__
+-#define __STOUT_HASHSET_HPP__
+-
+-#include <boost/get_pointer.hpp>
+-#include <boost/unordered_set.hpp>
+-
+-#include "foreach.hpp"
+-
+-
+-// Provides a hash set via Boost's 'unordered_set'. For most intensive
+-// purposes this could be accomplished with a templated typedef, but
+-// those don't exist (until C++-11). Also, doing it this way allows us
+-// to add functionality, or better naming of existing functionality,
+-// etc.
+-
+-template <typename Elem>
+-class hashset : public boost::unordered_set<Elem>
+-{
+-public:
+- // An explicit default constructor is needed so
+- // 'const hashset<T> map;' is not an error.
+- hashset() {}
+-
+- // Checks whether this map contains a binding for a key.
+- bool contains(const Elem& elem) const
+- {
+- return boost::unordered_set<Elem>::count(elem) > 0;
+- }
+-
+- // Checks whether there exists a value in this set that returns the
+- // a result equal to 'r' when the specified method is invoked.
+- template <typename R, typename T>
+- bool exists(R (T::*method)(), R r) const
+- {
+- foreach (const Elem& elem, *this) {
+- const T* t = boost::get_pointer(elem);
+- if (t->*method() == r) {
+- return true;
+- }
+- }
+- }
+-
+- // Checks whether there exists an element in this set whose
+- // specified member is equal to 'r'.
+- template <typename R, typename T>
+- bool exists(R (T::*member), R r) const
+- {
+- foreach (const Elem& elem, *this) {
+- const T* t = boost::get_pointer(elem);
+- if (t->*member == r) {
+- return true;
+- }
+- }
+- }
+-};
+-
+-
+-// Union operator.
+-template <typename Elem>
+-hashset<Elem> operator | (const hashset<Elem>& left, const hashset<Elem>& right)
+-{
+- // Note, we're not using 'set_union' since it affords us no benefit
+- // in efficiency and is more complicated to use given we have sets.
+- hashset<Elem> result = left;
+- result.insert(right.begin(), right.end());
+- return result;
+-}
+-
+-#endif // __STOUT_HASHSET_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/json.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/json.hpp
+deleted file mode 100644
+index 4406d07..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/json.hpp
++++ /dev/null
+@@ -1,205 +0,0 @@
+-#ifndef __STOUT_JSON__
+-#define __STOUT_JSON__
+-
+-#include <iomanip>
+-#include <iostream>
+-#include <limits>
+-#include <list>
+-#include <map>
+-#include <string>
+-
+-#include <boost/variant.hpp>
+-
+-#include <stout/foreach.hpp>
+-
+-// TODO(jsirois): Implement parsing that constructs JSON objects.
+-
+-// TODO(bmahler): Evaluate picojson / JSON_Spirit.
+-namespace JSON {
+-
+-// Implementation of the JavaScript Object Notation (JSON) grammar
+-// using boost::variant. We explicitly define each "type" of the
+-// grammar, including 'true' (json::True), 'false' (json::False), and
+-// 'null' (json::Null), for clarity and also because boost::variant
+-// "picks" the wrong type when we try and use std::string, long (or
+-// int), double (or float), and bool all in the same variant (while it
+-// does work with explicit casts, it seemed bad style to force people
+-// to put those casts in place). We could have avoided using
+-// json::String or json::Number and just used std::string and double
+-// respectively, but we choose to include them for completeness
+-// (although, this does pay a 2x cost when compiling thanks to all the
+-// extra template instantiations).
+-
+-struct String;
+-struct Number;
+-struct Object;
+-struct Array;
+-struct True;
+-struct False;
+-struct Null;
+-
+-
+-typedef boost::variant<boost::recursive_wrapper<String>,
+- boost::recursive_wrapper<Number>,
+- boost::recursive_wrapper<Object>,
+- boost::recursive_wrapper<Array>,
+- boost::recursive_wrapper<True>,
+- boost::recursive_wrapper<False>,
+- boost::recursive_wrapper<Null> > Value;
+-
+-
+-struct String
+-{
+- String() {}
+- String(const char* _value) : value(_value) {}
+- String(const std::string& _value) : value(_value) {}
+- std::string value;
+-};
+-
+-
+-struct Number
+-{
+- Number() : value(0) {}
+- Number(double _value) : value(_value) {}
+- double value;
+-};
+-
+-
+-struct Object
+-{
+- std::map<std::string, Value> values;
+-};
+-
+-
+-struct Array
+-{
+- std::list<Value> values;
+-};
+-
+-
+-struct True {};
+-
+-
+-struct False {};
+-
+-
+-struct Null {};
+-
+-
+-// Implementation of rendering JSON objects built above using standard
+-// C++ output streams. The visitor pattern is used thanks to to build
+-// a "renderer" with boost::static_visitor and two top-level render
+-// routines are provided for rendering JSON objects and arrays.
+-
+-struct Renderer : boost::static_visitor<>
+-{
+- Renderer(std::ostream& _out) : out(_out) {}
+-
+- void operator () (const String& string) const
+- {
+- // TODO(benh): This escaping DOES NOT handle unicode, it encodes as ASCII.
+- // See RFC4627 for the JSON string specificiation.
+- out << "\"";
+- foreach (unsigned char c, string.value) {
+- switch (c) {
+- case '"': out << "\\\""; break;
+- case '\\': out << "\\\\"; break;
+- case '/': out << "\\/"; break;
+- case '\b': out << "\\b"; break;
+- case '\f': out << "\\f"; break;
+- case '\n': out << "\\n"; break;
+- case '\r': out << "\\r"; break;
+- case '\t': out << "\\t"; break;
+- default:
+- // See RFC4627 for these ranges.
+- if ((c >= 0x20 && c <= 0x21) ||
+- (c >= 0x23 && c <= 0x5B) ||
+- (c >= 0x5D && c < 0x7F)) {
+- out << c;
+- } else {
+- // NOTE: We also escape all bytes > 0x7F since they imply more than
+- // 1 byte in UTF-8. This is why we don't escape UTF-8 properly.
+- // See RFC4627 for the escaping format: \uXXXX (X is a hex digit).
+- // Each byte here will be of the form: \u00XX (this is why we need
+- // setw and the cast to unsigned int).
+- out << "\\u" << std::setfill('0') << std::setw(4)
+- << std::hex << std::uppercase << (unsigned int) c;
+- }
+- break;
+- }
+- }
+- out << "\"";
+- }
+-
+- void operator () (const Number& number) const
+- {
+- // Use the guaranteed accurate precision, see:
+- // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2005.pdf
+- out << std::setprecision(std::numeric_limits<double>::digits10)
+- << number.value;
+- }
+-
+- void operator () (const Object& object) const
+- {
+- out << "{";
+- std::map<std::string, Value>::const_iterator iterator;
+- iterator = object.values.begin();
+- while (iterator != object.values.end()) {
+- out << "\"" << (*iterator).first << "\":";
+- boost::apply_visitor(Renderer(out), (*iterator).second);
+- if (++iterator != object.values.end()) {
+- out << ",";
+- }
+- }
+- out << "}";
+- }
+-
+- void operator () (const Array& array) const
+- {
+- out << "[";
+- std::list<Value>::const_iterator iterator;
+- iterator = array.values.begin();
+- while (iterator != array.values.end()) {
+- boost::apply_visitor(Renderer(out), *iterator);
+- if (++iterator != array.values.end()) {
+- out << ",";
+- }
+- }
+- out << "]";
+- }
+-
+- void operator () (const True&) const
+- {
+- out << "true";
+- }
+-
+- void operator () (const False&) const
+- {
+- out << "false";
+- }
+-
+- void operator () (const Null&) const
+- {
+- out << "null";
+- }
+-
+-private:
+- std::ostream& out;
+-};
+-
+-
+-inline void render(std::ostream& out, const Value& value)
+-{
+- boost::apply_visitor(Renderer(out), value);
+-}
+-
+-
+-inline std::ostream& operator<<(std::ostream& out, const JSON::Value& value)
+-{
+- JSON::render(out, value);
+- return out;
+-}
+-
+-} // namespace JSON {
+-
+-#endif // __STOUT_JSON__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/lambda.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/lambda.hpp
+deleted file mode 100644
+index d493353..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/lambda.hpp
++++ /dev/null
+@@ -1,14 +0,0 @@
+-#ifndef __STOUT_LAMBDA_HPP__
+-#define __STOUT_LAMBDA_HPP__
+-
+-#include <tr1/functional>
+-
+-namespace lambda {
+-
+-using std::tr1::bind;
+-using std::tr1::function;
+-using namespace std::tr1::placeholders;
+-
+-} // namespace lambda {
+-
+-#endif // __STOUT_LAMBDA_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/linkedhashmap.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/linkedhashmap.hpp
+deleted file mode 100644
+index a27ec26..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/linkedhashmap.hpp
++++ /dev/null
+@@ -1,92 +0,0 @@
+-#ifndef __STOUT_LINKEDHASHMAP_HPP__
+-#define __STOUT_LINKEDHASHMAP_HPP__
+-
+-#include <list>
+-#include <utility>
+-
+-#include <stout/hashmap.hpp>
+-#include <stout/option.hpp>
+-
+-// Implementation of a hashmap that maintains the insertion order
+-// of the keys. Note that re-insertion of a key (i.e., update)
+-// doesn't update its insertion order.
+-// TODO(vinod/bmahler): Consider extending from stout::hashmap and/or
+-// having a compatible API with stout::hashmap.
+-template <typename Key, typename Value>
+-class LinkedHashMap
+-{
+-public:
+- typedef std::list<Key> list;
+- typedef hashmap<Key, std::pair<Value, typename list::iterator> > map;
+-
+- Value& operator[] (const Key& key)
+- {
+- if (!values_.contains(key)) {
+- // Insert into the list and get the "pointer" into the list.
+- typename list::iterator i = keys_.insert(keys_.end(), key);
+- values_[key] = std::make_pair(Value(), i); // Store default value.
+- }
+- return values_[key].first;
+- }
+-
+- Option<Value> get(const Key& key) const
+- {
+- if (values_.contains(key)) {
+- return values_.at(key).first;
+- }
+- return None();
+- }
+-
+- bool contains(const Key& key) const
+- {
+- return values_.contains(key);
+- }
+-
+- size_t erase(const Key& key)
+- {
+- if (values_.contains(key)) {
+- // Get the "pointer" into the list.
+- typename list::iterator i = values_[key].second;
+- keys_.erase(i);
+- return values_.erase(key);
+- }
+- return 0;
+- }
+-
+- std::list<Key> keys() const
+- {
+- return keys_;
+- }
+-
+- std::list<Value> values() const
+- {
+- std::list<Value> result;
+- foreach (const Key& key, keys_) {
+- result.push_back(values_.at(key).first);
+- }
+- return result;
+- }
+-
+- size_t size() const
+- {
+- return keys_.size();
+- }
+-
+- bool empty() const
+- {
+- return keys_.empty();
+- }
+-
+- void clear()
+- {
+- values_.clear();
+- keys_.clear();
+- }
+-
+-private:
+- list keys_; // Keys ordered by the insertion order.
+- map values_; // Map of values and "pointers" to the linked list.
+-};
+-
+-
+-#endif // __STOUT_LINKEDHASHMAP_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/multihashmap.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/multihashmap.hpp
+deleted file mode 100644
+index 10e49dc..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/multihashmap.hpp
++++ /dev/null
+@@ -1,109 +0,0 @@
+-#ifndef __STOUT_MULTIHASHMAP_HPP__
+-#define __STOUT_MULTIHASHMAP_HPP__
+-
+-#include <algorithm> // For find.
+-#include <list>
+-#include <set>
+-#include <utility>
+-
+-#include <boost/unordered_map.hpp>
+-
+-
+-// Implementation of a hash multimap via Boost's 'unordered_multimap'
+-// but with a better interface. The rationale for creating this is
+-// that the std::multimap interface is painful to use (requires lots
+-// of iterator garbage, as well as the use of 'equal_range' which
+-// makes for cluttered code).
+-template <typename K, typename V>
+-class multihashmap : public boost::unordered_multimap<K, V>
+-{
+-public:
+- void put(const K& key, const V& value);
+- std::list<V> get(const K& key) const;
+- std::set<K> keys() const;
+- bool remove(const K& key);
+- bool remove(const K& key, const V& value);
+- bool contains(const K& key) const;
+- bool contains(const K& key, const V& value) const;
+-};
+-
+-
+-template <typename K, typename V>
+-void multihashmap<K, V>::put(const K& key, const V& value)
+-{
+- boost::unordered_multimap<K, V>::insert(std::pair<K, V>(key, value));
+-}
+-
+-
+-template <typename K, typename V>
+-std::list<V> multihashmap<K, V>::get(const K& key) const
+-{
+- std::list<V> values; // Values to return.
+-
+- std::pair<typename boost::unordered_multimap<K, V>::const_iterator,
+- typename boost::unordered_multimap<K, V>::const_iterator> range;
+-
+- range = boost::unordered_multimap<K, V>::equal_range(key);
+-
+- typename boost::unordered_multimap<K, V>::const_iterator i;
+- for (i = range.first; i != range.second; ++i) {
+- values.push_back(i->second);
+- }
+-
+- return values;
+-}
+-
+-
+-template <typename K, typename V>
+-std::set<K> multihashmap<K, V>::keys() const
+-{
+- std::set<K> keys;
+- foreachkey (const K& key, *this) {
+- keys.insert(key);
+- }
+- return keys;
+-}
+-
+-
+-template <typename K, typename V>
+-bool multihashmap<K, V>::remove(const K& key)
+-{
+- return boost::unordered_multimap<K, V>::erase(key) > 0;
+-}
+-
+-
+-template <typename K, typename V>
+-bool multihashmap<K, V>::remove(const K& key, const V& value)
+-{
+- std::pair<typename boost::unordered_multimap<K, V>::iterator,
+- typename boost::unordered_multimap<K, V>::iterator> range;
+-
+- range = boost::unordered_multimap<K, V>::equal_range(key);
+-
+- typename boost::unordered_multimap<K, V>::iterator i;
+- for (i = range.first; i != range.second; ++i) {
+- if (i->second == value) {
+- boost::unordered_multimap<K, V>::erase(i);
+- return true;
+- }
+- }
+-
+- return false;
+-}
+-
+-
+-template <typename K, typename V>
+-bool multihashmap<K, V>::contains(const K& key) const
+-{
+- return multihashmap<K, V>::count(key) > 0;
+-}
+-
+-
+-template <typename K, typename V>
+-bool multihashmap<K, V>::contains(const K& key, const V& value) const
+-{
+- const std::list<V>& values = get(key);
+- return std::find(values.begin(), values.end(), value) != values.end();
+-}
+-
+-#endif // __STOUT_MULTIHASHMAP_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/multimap.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/multimap.hpp
+deleted file mode 100644
+index 187ad79..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/multimap.hpp
++++ /dev/null
+@@ -1,107 +0,0 @@
+-#ifndef __STOUT_MULTIMAP_HPP__
+-#define __STOUT_MULTIMAP_HPP__
+-
+-#include <algorithm>
+-#include <list>
+-#include <map>
+-#include <set>
+-#include <utility>
+-
+-// Implementation of a multimap via std::multimap but with a better
+-// interface. The rationale for creating this is that the
+-// std::multimap interface is painful to use (requires lots of
+-// iterator garbage, as well as the use of 'equal_range' which makes
+-// for cluttered code).
+-template <typename K, typename V>
+-class Multimap : public std::multimap<K, V>
+-{
+-public:
+- void put(const K& key, const V& value);
+- std::list<V> get(const K& key) const;
+- std::set<K> keys() const;
+- bool remove(const K& key);
+- bool remove(const K& key, const V& value);
+- bool contains(const K& key) const;
+- bool contains(const K& key, const V& value) const;
+-};
+-
+-
+-template <typename K, typename V>
+-void Multimap<K, V>::put(const K& key, const V& value)
+-{
+- std::multimap<K, V>::insert(std::pair<K, V>(key, value));
+-}
+-
+-
+-template <typename K, typename V>
+-std::list<V> Multimap<K, V>::get(const K& key) const
+-{
+- std::list<V> values; // Values to return.
+-
+- std::pair<typename std::multimap<K, V>::const_iterator,
+- typename std::multimap<K, V>::const_iterator> range;
+-
+- range = std::multimap<K, V>::equal_range(key);
+-
+- typename std::multimap<K, V>::const_iterator i;
+- for (i = range.first; i != range.second; ++i) {
+- values.push_back(i->second);
+- }
+-
+- return values;
+-}
+-
+-
+-template <typename K, typename V>
+-std::set<K> Multimap<K, V>::keys() const
+-{
+- std::set<K> keys;
+- foreachkey (const K& key, *this) {
+- keys.insert(key);
+- }
+- return keys;
+-}
+-
+-
+-template <typename K, typename V>
+-bool Multimap<K, V>::remove(const K& key)
+-{
+- return std::multimap<K, V>::erase(key) > 0;
+-}
+-
+-
+-template <typename K, typename V>
+-bool Multimap<K, V>::remove(const K& key, const V& value)
+-{
+- std::pair<typename std::multimap<K, V>::iterator,
+- typename std::multimap<K, V>::iterator> range;
+-
+- range = std::multimap<K, V>::equal_range(key);
+-
+- typename std::multimap<K, V>::iterator i;
+- for (i = range.first; i != range.second; ++i) {
+- if (i->second == value) {
+- std::multimap<K, V>::erase(i);
+- return true;
+- }
+- }
+-
+- return false;
+-}
+-
+-
+-template <typename K, typename V>
+-bool Multimap<K, V>::contains(const K& key) const
+-{
+- return std::multimap<K, V>::count(key) > 0;
+-}
+-
+-
+-template <typename K, typename V>
+-bool Multimap<K, V>::contains(const K& key, const V& value) const
+-{
+- const std::list<V>& values = get(key);
+- return std::find(values.begin(), values.end(), value) != values.end();
+-}
+-
+-#endif // __STOUT_MULTIMAP_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/net.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/net.hpp
+deleted file mode 100644
+index d03de5a..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/net.hpp
++++ /dev/null
+@@ -1,96 +0,0 @@
+-#ifndef __STOUT_NET_HPP__
+-#define __STOUT_NET_HPP__
+-
+-#include <netdb.h>
+-#include <stdio.h>
+-
+-#include <sys/param.h>
+-#include <sys/socket.h>
+-#include <sys/types.h>
+-
+-#include <curl/curl.h>
+-
+-#include <string>
+-
+-#include "error.hpp"
+-#include "os.hpp"
+-#include "try.hpp"
+-
+-
+-// Network utilities.
+-namespace net {
+-
+-// Returns the HTTP response code resulting from attempting to download the
+-// specified HTTP or FTP URL into a file at the specified path.
+-inline Try<int> download(const std::string& url, const std::string& path)
+-{
+- Try<int> fd = os::open(
+- path, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IRWXO);
+-
+- if (fd.isError()) {
+- return Error(fd.error());
+- }
+-
+- curl_global_init(CURL_GLOBAL_ALL);
+- CURL* curl = curl_easy_init();
+-
+- if (curl == NULL) {
+- curl_easy_cleanup(curl);
+- os::close(fd.get());
+- return Error("Failed to initialize libcurl");
+- }
+-
+- curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
+- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
+-
+- FILE* file = fdopen(fd.get(), "w");
+- if (file == NULL) {
+- return ErrnoError("Failed to open file handle of '" + path + "'");
+- }
+- curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);
+-
+- CURLcode curlErrorCode = curl_easy_perform(curl);
+- if (curlErrorCode != 0) {
+- curl_easy_cleanup(curl);
+- fclose(file);
+- return Error(curl_easy_strerror(curlErrorCode));
+- }
+-
+- long code;
+- curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
+- curl_easy_cleanup(curl);
+-
+- if (fclose(file) != 0) {
+- return ErrnoError("Failed to close file handle of '" + path + "'");
+- }
+-
+- return Try<int>::some(code);
+-}
+-
+-// Returns a Try of the hostname for the provided IP. If the hostname cannot
+-// be resolved, then a string version of the IP address is returned.
+-inline Try<std::string> getHostname(uint32_t ip)
+-{
+- sockaddr_in addr;
+- memset(&addr, 0, sizeof(addr));
+- addr.sin_family = AF_INET;
+- addr.sin_addr.s_addr = ip;
+-
+- char hostname[MAXHOSTNAMELEN];
+- if (getnameinfo(
+- (sockaddr*)&addr,
+- sizeof(addr),
+- hostname,
+- MAXHOSTNAMELEN,
+- NULL,
+- 0,
+- 0) != 0) {
+- return ErrnoError();
+- }
+-
+- return std::string(hostname);
+-}
+-
+-} // namespace net {
+-
+-#endif // __STOUT_NET_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/none.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/none.hpp
+deleted file mode 100644
+index ea8e0f5..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/none.hpp
++++ /dev/null
+@@ -1,56 +0,0 @@
+-#ifndef __STOUT_NONE_HPP__
+-#define __STOUT_NONE_HPP__
+-
+-#include "option.hpp"
+-#include "result.hpp"
+-
+-// A "none" type that is implicitly convertible to an Option<T> and
+-// Result<T> for any T (effectively "syntactic sugar" to make code
+-// more readable). The implementation uses cast operators to perform
+-// the conversions instead of adding constructors to Option/Result
+-// directly. Performance shouldn't be an issue given that an instance
+-// of None has no virtual functions and no fields.
+-
+-class None
+-{
+-public:
+- template <typename T>
+- operator Option<T> () const
+- {
+- return Option<T>::none();
+- }
+-
+- // Give the compiler some help for nested Option<T>. For example,
+- // enable converting None to a Try<Option<T>>. Note that this will
+- // bind to the innermost Option<T>.
+- template <template <typename> class S, typename T>
+- operator S<Option<T> > () const
+- {
+- return S<Option<T> >(Option<T>::none());
+- }
+-
+- template <typename T>
+- operator Result<T> () const
+- {
+- return Result<T>::none();
+- }
+-
+- // Give the compiler some help for nested Result<T>. For example,
+- // enable converting None to a Try<Result<T>>. Note that this will
+- // bind to the innermost Result<T>.
+- template <template <typename> class S, typename T>
+- operator S<Result<T> > () const
+- {
+- return S<Result<T> >(Result<T>::none());
+- }
+-
+- // Give the compiler some more help to disambiguate the above cast
+- // operators from Result<Option<T>>.
+- template <typename T>
+- operator Result<Option<T> > () const
+- {
+- return Result<Option<T> >::none();
+- }
+-};
+-
+-#endif // __STOUT_NONE_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/nothing.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/nothing.hpp
+deleted file mode 100644
+index d0f925d..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/nothing.hpp
++++ /dev/null
+@@ -1,6 +0,0 @@
+-#ifndef __STOUT_NOTHING_HPP__
+-#define __STOUT_NOTHING_HPP__
+-
+-struct Nothing {};
+-
+-#endif // __STOUT_NOTHING_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/numify.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/numify.hpp
+deleted file mode 100644
+index d23e238..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/numify.hpp
++++ /dev/null
+@@ -1,40 +0,0 @@
+-#ifndef __STOUT_NUMIFY_HPP__
+-#define __STOUT_NUMIFY_HPP__
+-
+-#include <string>
+-
+-#include <boost/lexical_cast.hpp>
+-
+-#include "error.hpp"
+-#include "none.hpp"
+-#include "option.hpp"
+-#include "result.hpp"
+-#include "try.hpp"
+-
+-template <typename T>
+-Try<T> numify(const std::string& s)
+-{
+- try {
+- return boost::lexical_cast<T>(s);
+- } catch (const boost::bad_lexical_cast&) {
+- return Error("Failed to convert '" + s + "' to number");
+- }
+-}
+-
+-
+-template <typename T>
+-Result<T> numify(const Option<std::string>& s)
+-{
+- if (s.isSome()) {
+- Try<T> t = numify<T>(s.get());
+- if (t.isSome()) {
+- return t.get();
+- } else if (t.isError()) {
+- return Error(t.error());
+- }
+- }
+-
+- return None();
+-}
+-
+-#endif // __STOUT_NUMIFY_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/option.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/option.hpp
+deleted file mode 100644
+index 42b75a5..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/option.hpp
++++ /dev/null
+@@ -1,96 +0,0 @@
+-#ifndef __STOUT_OPTION_HPP__
+-#define __STOUT_OPTION_HPP__
+-
+-#include <assert.h>
+-
+-#include <stout/result.hpp>
+-
+-template <typename T>
+-class Option
+-{
+-public:
+- static Option<T> none()
+- {
+- return Option<T>(NONE);
+- }
+-
+- static Option<T> some(const T& t)
+- {
+- return Option<T>(SOME, new T(t));
+- }
+-
+- Option() : state(NONE), t(NULL) {}
+-
+- Option(const T& _t) : state(SOME), t(new T(_t)) {}
+-
+- Option(const Option<T>& that)
+- {
+- state = that.state;
+- if (that.t != NULL) {
+- t = new T(*that.t);
+- } else {
+- t = NULL;
+- }
+- }
+-
+- ~Option()
+- {
+- delete t;
+- }
+-
+- operator Result<T> () const
+- {
+- if (isNone()) {
+- return Result<T>::none();
+- }
+-
+- return Result<T>::some(get());
+- }
+-
+- Option<T>& operator = (const Option<T>& that)
+- {
+- if (this != &that) {
+- delete t;
+- state = that.state;
+- if (that.t != NULL) {
+- t = new T(*that.t);
+- } else {
+- t = NULL;
+- }
+- }
+-
+- return *this;
+- }
+-
+- bool operator == (const Option<T>& that) const
+- {
+- return (state == NONE && that.state == NONE) ||
+- (state == SOME && that.state == SOME && *t == *that.t);
+- }
+-
+- bool operator != (const Option<T>& that) const
+- {
+- return !operator == (that);
+- }
+-
+- bool isSome() const { return state == SOME; }
+- bool isNone() const { return state == NONE; }
+-
+- T get() const { assert(state == SOME); return *t; }
+-
+- T get(const T& _t) const { return state == NONE ? _t : *t; }
+-
+-private:
+- enum State {
+- SOME,
+- NONE,
+- };
+-
+- Option(State _state, T* _t = NULL)
+- : state(_state), t(_t) {}
+-
+- State state;
+- T* t;
+-};
+-
+-#endif // __STOUT_OPTION_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/os.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/os.hpp
+deleted file mode 100644
+index 544cf8c..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/os.hpp
++++ /dev/null
+@@ -1,1056 +0,0 @@
+-#ifndef __STOUT_OS_HPP__
+-#define __STOUT_OS_HPP__
+-
+-#ifdef __APPLE__
+-#include <crt_externs.h> // For _NSGetEnviron().
+-#endif
+-#include <dirent.h>
+-#include <errno.h>
+-#include <fcntl.h>
+-#include <fts.h>
+-#include <glob.h>
+-#include <libgen.h>
+-#include <limits.h>
+-#include <netdb.h>
+-#include <pwd.h>
+-#include <signal.h>
+-#include <stddef.h>
+-#include <stdio.h>
+-#include <stdlib.h>
+-#include <unistd.h>
+-#include <utime.h>
+-
+-#include <glog/logging.h>
+-
+-#ifdef __linux__
+-#include <linux/version.h>
+-#endif // __linux__
+-
+-#include <sys/stat.h>
+-#include <sys/statvfs.h>
+-#ifdef __linux__
+-#include <sys/sysinfo.h>
+-#endif // __linux__
+-#include <sys/types.h>
+-#include <sys/utsname.h>
+-
+-#include <list>
+-#include <set>
+-#include <sstream>
+-#include <string>
+-
+-#include <stout/bytes.hpp>
+-#include <stout/duration.hpp>
+-#include <stout/error.hpp>
+-#include <stout/foreach.hpp>
+-#include <stout/none.hpp>
+-#include <stout/nothing.hpp>
+-#include <stout/option.hpp>
+-#include <stout/path.hpp>
+-#include <stout/result.hpp>
+-#include <stout/strings.hpp>
+-#include <stout/try.hpp>
+-
+-#include <stout/os/exists.hpp>
+-#include <stout/os/fork.hpp>
+-#include <stout/os/killtree.hpp>
+-#ifdef __linux__
+-#include <stout/os/linux.hpp>
+-#endif // __linux__
+-#include <stout/os/ls.hpp>
+-#ifdef __APPLE__
+-#include <stout/os/osx.hpp>
+-#endif // __APPLE__
+-#include <stout/os/pstree.hpp>
+-#include <stout/os/read.hpp>
+-#include <stout/os/sendfile.hpp>
+-#include <stout/os/signals.hpp>
+-#ifdef __APPLE__
+-#include <stout/os/sysctl.hpp>
+-#endif // __APPLE__
+-
+-#ifdef __APPLE__
+-// Assigning the result pointer to ret silences an unused var warning.
+-#define gethostbyname2_r(name, af, ret, buf, buflen, result, h_errnop) \
+- ({ (void)ret; *(result) = gethostbyname2(name, af); 0; })
+-#endif // __APPLE__
+-
+-// Need to declare 'environ' pointer for non OS X platforms.
+-#ifndef __APPLE__
+-extern char** environ;
+-#endif
+-
+-namespace os {
+-
+-inline char** environ()
+-{
+- // Accessing the list of environment variables is platform-specific.
+- // On OS X, the 'environ' symbol isn't visible to shared libraries,
+- // so we must use the _NSGetEnviron() function (see 'man environ' on
+- // OS X). On other platforms, it's fine to access 'environ' from
+- // shared libraries.
+-#ifdef __APPLE__
+- return *_NSGetEnviron();
+-#else
+- return ::environ;
+-#endif
+-}
+-
+-
+-// Checks if the specified key is in the environment variables.
+-inline bool hasenv(const std::string& key)
+-{
+- char* value = ::getenv(key.c_str());
+-
+- return value != NULL;
+-}
+-
+-// Looks in the environment variables for the specified key and
+-// returns a string representation of it's value. If 'expected' is
+-// true (default) and no environment variable matching key is found,
+-// this function will exit the process.
+-inline std::string getenv(const std::string& key, bool expected = true)
+-{
+- char* value = ::getenv(key.c_str());
+-
+- if (expected && value == NULL) {
+- LOG(FATAL) << "Expecting '" << key << "' in environment variables";
+- }
+-
+- if (value != NULL) {
+- return std::string(value);
+- }
+-
+- return std::string();
+-}
+-
+-
+-// Sets the value associated with the specified key in the set of
+-// environment variables.
+-inline void setenv(const std::string& key,
+- const std::string& value,
+- bool overwrite = true)
+-{
+- ::setenv(key.c_str(), value.c_str(), overwrite ? 1 : 0);
+-}
+-
+-
+-// Unsets the value associated with the specified key in the set of
+-// environment variables.
+-inline void unsetenv(const std::string& key)
+-{
+- ::unsetenv(key.c_str());
+-}
+-
+-
+-inline Try<bool> access(const std::string& path, int how)
+-{
+- if (::access(path.c_str(), how) < 0) {
+- if (errno == EACCES) {
+- return false;
+- } else {
+- return ErrnoError();
+- }
+- }
+- return true;
+-}
+-
+-
+-inline Try<int> open(const std::string& path, int oflag, mode_t mode = 0)
+-{
+- int fd = ::open(path.c_str(), oflag, mode);
+-
+- if (fd < 0) {
+- return ErrnoError();
+- }
+-
+- return fd;
+-}
+-
+-
+-inline Try<Nothing> close(int fd)
+-{
+- if (::close(fd) != 0) {
+- return ErrnoError();
+- }
+-
+- return Nothing();
+-}
+-
+-
+-inline Try<Nothing> cloexec(int fd)
+-{
+- int flags = ::fcntl(fd, F_GETFD);
+-
+- if (flags == -1) {
+- return ErrnoError();
+- }
+-
+- if (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
+- return ErrnoError();
+- }
+-
+- return Nothing();
+-}
+-
+-
+-inline Try<Nothing> nonblock(int fd)
+-{
+- int flags = ::fcntl(fd, F_GETFL);
+-
+- if (flags == -1) {
+- return ErrnoError();
+- }
+-
+- if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
+- return ErrnoError();
+- }
+-
+- return Nothing();
+-}
+-
+-
+-inline Try<bool> isNonblock(int fd)
+-{
+- int flags = ::fcntl(fd, F_GETFL);
+-
+- if (flags == -1) {
+- return ErrnoError();
+- }
+-
+- return (flags & O_NONBLOCK) != 0;
+-}
+-
+-
+-// Sets the access and modification times of 'path' to the current time.
+-inline Try<Nothing> utime(const std::string& path)
+-{
+- if (::utime(path.c_str(), NULL) == -1) {
+- return ErrnoError();
+- }
+-
+- return Nothing();
+-}
+-
+-
+-inline Try<Nothing> touch(const std::string& path)
+-{
+- if (!exists(path)) {
+- Try<int> fd =
+- open(path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IRWXO);
+-
+- if (fd.isError()) {
+- return Error("Failed to open file: " + fd.error());
+- }
+-
+- return close(fd.get());
+- }
+-
+- // Update the access and modification times.
+- return utime(path);
+-}
+-
+-
+-// Creates a temporary file using the specified path template. The
+-// template may be any path with _6_ `Xs' appended to it, for example
+-// /tmp/temp.XXXXXX. The trailing `Xs' are replaced with a unique
+-// alphanumeric combination.
+-inline Try<std::string> mktemp(const std::string& path = "/tmp/XXXXXX")
+-{
+- char* temp = new char[path.size() + 1];
+- int fd = ::mkstemp(::strcpy(temp, path.c_str()));
+-
+- if (fd < 0) {
+- delete temp;
+- return ErrnoError();
+- }
+-
+- // We ignore the return value of close(). This is because users
+- // calling this function are interested in the return value of
+- // mkstemp(). Also an unsuccessful close() doesn't affect the file.
+- os::close(fd);
+-
+- std::string result(temp);
+- delete temp;
+- return result;
+-}
+-
+-
+-// Write out the string to the file at the current fd position.
+-inline Try<Nothing> write(int fd, const std::string& message)
+-{
+- size_t offset = 0;
+-
+- while (offset < message.length()) {
+- ssize_t length =
+- ::write(fd, message.data() + offset, message.length() - offset);
+-
+- if (length < 0) {
+- // TODO(benh): Handle a non-blocking fd? (EAGAIN, EWOULDBLOCK)
+- if (errno == EINTR) {
+- continue;
+- }
+- return ErrnoError();
+- }
+-
+- offset += length;
+- }
+-
+- return Nothing();
+-}
+-
+-
+-// A wrapper function that wraps the above write() with
+-// open and closing the file.
+-inline Try<Nothing> write(const std::string& path, const std::string& message)
+-{
+- Try<int> fd = os::open(path, O_WRONLY | O_CREAT | O_TRUNC,
+- S_IRUSR | S_IWUSR | S_IRGRP | S_IRWXO);
+- if (fd.isError()) {
+- return ErrnoError("Failed to open file '" + path + "'");
+- }
+-
+- Try<Nothing> result = write(fd.get(), message);
+-
+- // We ignore the return value of close(). This is because users
+- // calling this function are interested in the return value of
+- // write(). Also an unsuccessful close() doesn't affect the write.
+- os::close(fd.get());
+-
+- return result;
+-}
+-
+-
+-inline Try<Nothing> rm(const std::string& path)
+-{
+- if (::remove(path.c_str()) != 0) {
+- return ErrnoError();
+- }
+-
+- return Nothing();
+-}
+-
+-
+-inline Try<std::string> basename(const std::string& path)
+-{
+- char* temp = new char[path.size() + 1];
+- char* result = ::basename(::strcpy(temp, path.c_str()));
+- if (result == NULL) {
+- delete[] temp;
+- return ErrnoError();
+- }
+-
+- std::string s(result);
+- delete[] temp;
+- return s;
+-}
+-
+-
+-inline Try<std::string> dirname(const std::string& path)
+-{
+- char* temp = new char[path.size() + 1];
+- char* result = ::dirname(::strcpy(temp, path.c_str()));
+- if (result == NULL) {
+- delete[] temp;
+- return ErrnoError();
+- }
+-
+- std::string s(result);
+- delete[] temp;
+- return s;
+-}
+-
+-
+-inline Result<std::string> realpath(const std::string& path)
+-{
+- char temp[PATH_MAX];
+- if (::realpath(path.c_str(), temp) == NULL) {
+- if (errno == ENOENT || errno == ENOTDIR) {
+- return None();
+- }
+- return ErrnoError();
+- }
+- return std::string(temp);
+-}
+-
+-
+-inline bool isdir(const std::string& path)
+-{
+- struct stat s;
+-
+- if (::stat(path.c_str(), &s) < 0) {
+- return false;
+- }
+- return S_ISDIR(s.st_mode);
+-}
+-
+-
+-inline bool isfile(const std::string& path)
+-{
+- struct stat s;
+-
+- if (::stat(path.c_str(), &s) < 0) {
+- return false;
+- }
+- return S_ISREG(s.st_mode);
+-}
+-
+-
+-inline bool islink(const std::string& path)
+-{
+- struct stat s;
+-
+- if (::lstat(path.c_str(), &s) < 0) {
+- return false;
+- }
+- return S_ISLNK(s.st_mode);
+-}
+-
+-
+-// TODO(benh): Put this in the 'paths' or 'files' or 'fs' namespace.
+-inline Try<long> mtime(const std::string& path)
+-{
+- struct stat s;
+-
+- if (::lstat(path.c_str(), &s) < 0) {
+- return ErrnoError("Error invoking stat for '" + path + "'");
+- }
+-
+- return s.st_mtime;
+-}
+-
+-
+-inline Try<Nothing> mkdir(const std::string& directory, bool recursive = true)
+-{
+- if (!recursive) {
+- if (::mkdir(directory.c_str(), 0755) < 0) {
+- return ErrnoError();
+- }
+- } else {
+- std::vector<std::string> tokens = strings::tokenize(directory, "/");
+- std::string path = "";
+-
+- // We got an absolute path, so keep the leading slash.
+- if (directory.find_first_of("/") == 0) {
+- path = "/";
+- }
+-
+- foreach (const std::string& token, tokens) {
+- path += token;
+- if (::mkdir(path.c_str(), 0755) < 0 && errno != EEXIST) {
+- return ErrnoError();
+- }
+- path += "/";
+- }
+- }
+-
+- return Nothing();
+-}
+-
+-// Creates a temporary directory using the specified path
+-// template. The template may be any path with _6_ `Xs' appended to
+-// it, for example /tmp/temp.XXXXXX. The trailing `Xs' are replaced
+-// with a unique alphanumeric combination.
+-inline Try<std::string> mkdtemp(const std::string& path = "/tmp/XXXXXX")
+-{
+- char* temp = new char[path.size() + 1];
+- if (::mkdtemp(::strcpy(temp, path.c_str())) != NULL) {
+- std::string result(temp);
+- delete[] temp;
+- return result;
+- } else {
+- delete[] temp;
+- return ErrnoError();
+- }
+-}
+-
+-// By default, recursively deletes a directory akin to: 'rm -r'. If the
+-// programmer sets recursive to false, it deletes a directory akin to: 'rmdir'.
+-// Note that this function expects an absolute path.
+-inline Try<Nothing> rmdir(const std::string& directory, bool recursive = true)
+-{
+- if (!recursive) {
+- if (::rmdir(directory.c_str()) < 0) {
+- return ErrnoError();
+- }
+- } else {
+- char* paths[] = {const_cast<char*>(directory.c_str()), NULL};
+-
+- FTS* tree = fts_open(paths, FTS_NOCHDIR, NULL);
+- if (tree == NULL) {
+- return ErrnoError();
+- }
+-
+- FTSENT* node;
+- while ((node = fts_read(tree)) != NULL) {
+- switch (node->fts_info) {
+- case FTS_DP:
+- if (::rmdir(node->fts_path) < 0 && errno != ENOENT) {
+- return ErrnoError();
+- }
+- break;
+- case FTS_F:
+- case FTS_SL:
+- if (::unlink(node->fts_path) < 0 && errno != ENOENT) {
+- return ErrnoError();
+- }
+- break;
+- default:
+- break;
+- }
+- }
+-
+- if (errno != 0) {
+- return ErrnoError();
+- }
+-
+- if (fts_close(tree) < 0) {
+- return ErrnoError();
+- }
+- }
+-
+- return Nothing();
+-}
+-
+-
+-inline int system(const std::string& command)
+-{
+- return ::system(command.c_str());
+-}
+-
+-
+-// TODO(bmahler): Clean these bool functions to return Try<Nothing>.
+-// Changes the specified path's user and group ownership to that of
+-// the specified user..
+-inline Try<Nothing> chown(
+- const std::string& user,
+- const std::string& path,
+- bool recursive = true)
+-{
+- passwd* passwd;
+- if ((passwd = ::getpwnam(user.c_str())) == NULL) {
+- return ErrnoError("Failed to get user information for '" + user + "'");
+- }
+-
+- if (recursive) {
+- // TODO(bmahler): Consider walking the file tree instead. We would need
+- // to be careful to not miss dotfiles.
+- std::string command = "chown -R " + stringify(passwd->pw_uid) + ':' +
+- stringify(passwd->pw_gid) + " '" + path + "'";
+-
+- int status = os::system(command);
+- if (status != 0) {
+- return ErrnoError(
+- "Failed to execute '" + command +
+- "' (exit status: " + stringify(status) + ")");
+- }
+- } else {
+- if (::chown(path.c_str(), passwd->pw_uid, passwd->pw_gid) < 0) {
+- return ErrnoError();
+- }
+- }
+-
+- return Nothing();
+-}
+-
+-
+-inline bool chmod(const std::string& path, int mode)
+-{
+- if (::chmod(path.c_str(), mode) < 0) {
+- PLOG(ERROR) << "Failed to changed the mode of the path '" << path << "'";
+- return false;
+- }
+-
+- return true;
+-}
+-
+-
+-inline bool chdir(const std::string& directory)
+-{
+- if (::chdir(directory.c_str()) < 0) {
+- PLOG(ERROR) << "Failed to change directory";
+- return false;
+- }
+-
+- return true;
+-}
+-
+-
+-inline bool su(const std::string& user)
+-{
+- passwd* passwd;
+- if ((passwd = ::getpwnam(user.c_str())) == NULL) {
+- PLOG(ERROR) << "Failed to get user information for '"
+- << user << "', getpwnam";
+- return false;
+- }
+-
+- if (::setgid(passwd->pw_gid) < 0) {
+- PLOG(ERROR) << "Failed to set group id, setgid";
+- return false;
+- }
+-
+- if (::setuid(passwd->pw_uid) < 0) {
+- PLOG(ERROR) << "Failed to set user id, setuid";
+- return false;
+- }
+-
+- return true;
+-}
+-
+-
+-inline std::string getcwd()
+-{
+- size_t size = 100;
+-
+- while (true) {
+- char* temp = new char[size];
+- if (::getcwd(temp, size) == temp) {
+- std::string result(temp);
+- delete[] temp;
+- return result;
+- } else {
+- if (errno != ERANGE) {
+- delete[] temp;
+- return std::string();
+- }
+- size *= 2;
+- delete[] temp;
+- }
+- }
+-
+- return std::string();
+-}
+-
+-
+-// Return the list of file paths that match the given pattern by recursively
+-// searching the given directory. A match is successful if the pattern is a
+-// substring of the file name.
+-// NOTE: Directory path should not end with '/'.
+-// NOTE: Symbolic links are not followed.
+-// TODO(vinod): Support regular expressions for pattern.
+-// TODO(vinod): Consider using ftw or a non-recursive approach.
+-inline Try<std::list<std::string> > find(
+- const std::string& directory,
+- const std::string& pattern)
+-{
+- std::list<std::string> results;
+-
+- if (!isdir(directory)) {
+- return Error("'" + directory + "' is not a directory");
+- }
+-
+- foreach (const std::string& entry, ls(directory)) {
+- std::string path = path::join(directory, entry);
+- // If it's a directory, recurse.
+- if (isdir(path) && !islink(path)) {
+- Try<std::list<std::string> > matches = find(path, pattern);
+- if (matches.isError()) {
+- return matches;
+- }
+- foreach (const std::string& match, matches.get()) {
+- results.push_back(match);
+- }
+- } else {
+- if (entry.find(pattern) != std::string::npos) {
+- results.push_back(path); // Matched the file pattern!
+- }
+- }
+- }
+-
+- return results;
+-}
+-
+-
+-inline std::string user()
+-{
+- passwd* passwd;
+- if ((passwd = getpwuid(getuid())) == NULL) {
+- LOG(FATAL) << "Failed to get username information";
+- }
+-
+- return passwd->pw_name;
+-}
+-
+-
+-inline Try<std::string> hostname()
+-{
+- char host[512];
+-
+- if (gethostname(host, sizeof(host)) < 0) {
+- return ErrnoError();
+- }
+-
+- // Allocate temporary buffer for gethostbyname2_r.
+- size_t length = 1024;
+- char* temp = new char[length];
+-
+- struct hostent he, *hep = NULL;
+- int result = 0;
+- int herrno = 0;
+-
+- while ((result = gethostbyname2_r(host, AF_INET, &he, temp,
+- length, &hep, &herrno)) == ERANGE) {
+- // Enlarge the buffer.
+- delete[] temp;
+- length *= 2;
+- temp = new char[length];
+- }
+-
+- if (result != 0 || hep == NULL) {
+- delete[] temp;
+- return Error(hstrerror(herrno));
+- }
+-
+- std::string hostname = hep->h_name;
+- delete[] temp;
+- return hostname;
+-}
+-
+-
+-// Runs a shell command formatted with varargs and return the return value
+-// of the command. Optionally, the output is returned via an argument.
+-// TODO(vinod): Pass an istream object that can provide input to the command.
+-inline Try<int> shell(std::ostream* os, const std::string& fmt, ...)
+-{
+- va_list args;
+- va_start(args, fmt);
+-
+- const Try<std::string>& cmdline = strings::internal::format(fmt, args);
+-
+- va_end(args);
+-
+- if (cmdline.isError()) {
+- return Error(cmdline.error());
+- }
+-
+- FILE* file;
+-
+- if ((file = popen(cmdline.get().c_str(), "r")) == NULL) {
+- return Error("Failed to run '" + cmdline.get() + "'");
+- }
+-
+- char line[1024];
+- // NOTE(vinod): Ideally the if and while loops should be interchanged. But
+- // we get a broken pipe error if we don't read the output and simply close.
+- while (fgets(line, sizeof(line), file) != NULL) {
+- if (os != NULL) {
+- *os << line ;
+- }
+- }
+-
+- if (ferror(file) != 0) {
+- ErrnoError error("Error reading output of '" + cmdline.get() + "'");
+- pclose(file); // Ignoring result since we already have an error.
+- return error;
+- }
+-
+- int status;
+- if ((status = pclose(file)) == -1) {
+- return Error("Failed to get status of '" + cmdline.get() + "'");
+- }
+-
+- return status;
+-}
+-
+-
+-// Suspends execution for the given duration.
+-inline Try<Nothing> sleep(const Duration& duration)
+-{
+- timespec remaining;
+- remaining.tv_sec = static_cast<long>(duration.secs());
+- remaining.tv_nsec =
+- static_cast<long>((duration - Seconds(remaining.tv_sec)).ns());
+-
+- while (nanosleep(&remaining, &remaining) == -1) {
+- if (errno == EINTR) {
+- continue;
+- } else {
+- return ErrnoError();
+- }
+- }
+-
+- return Nothing();
+-}
+-
+-
+-// Creates a tar 'archive' with gzip compression, of the given 'path'.
+-inline Try<Nothing> tar(const std::string& path, const std::string& archive)
+-{
+- Try<int> status =
+- shell(NULL, "tar -czf %s %s", archive.c_str(), path.c_str());
+-
+- if (status.isError()) {
+- return Error("Failed to archive " + path + ": " + status.error());
+- } else if (status.get() != 0) {
+- return Error("Non-zero exit status when archiving " + path +
+- ": " + stringify(status.get()));
+- }
+-
+- return Nothing();
+-}
+-
+-
+-// Returns the list of files that match the given (shell) pattern.
+-inline Try<std::list<std::string> > glob(const std::string& pattern)
+-{
+- glob_t g;
+- int status = ::glob(pattern.c_str(), GLOB_NOSORT, NULL, &g);
+-
+- std::list<std::string> result;
+-
+- if (status != 0) {
+- if (status == GLOB_NOMATCH) {
+- return result; // Empty list.
+- } else {
+- return ErrnoError();
+- }
+- }
+-
+- for (size_t i = 0; i < g.gl_pathc; ++i) {
+- result.push_back(g.gl_pathv[i]);
+- }
+-
+- globfree(&g); // Best-effort free of dynamically allocated memory.
+-
+- return result;
+-}
+-
+-
+-// Returns the total number of cpus (cores).
+-inline Try<long> cpus()
+-{
+- long cpus = sysconf(_SC_NPROCESSORS_ONLN);
+-
+- if (cpus < 0) {
+- return ErrnoError();
+- }
+- return cpus;
+-}
+-
+-
+-// Returns the total size of main memory.
+-inline Try<Bytes> memory()
+-{
+-#ifdef __linux__
+- struct sysinfo info;
+- if (sysinfo(&info) != 0) {
+- return ErrnoError();
+- }
+-# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 23)
+- return Bytes(info.totalram * info.mem_unit);
+-# else
+- return Bytes(info.totalram);
+-# endif
+-#elif defined __APPLE__
+- const Try<int64_t>& memory =
+- os::sysctl(CTL_HW, HW_MEMSIZE).integer();
+-
+- if (memory.isError()) {
+- return Error(memory.error());
+- }
+- return Bytes(memory.get());
+-#else
+- return Error("Cannot determine the size of main memory");
+-#endif
+-}
+-
+-
+-// The structure returned by uname describing the currently running system.
+-struct UTSInfo
+-{
+- std::string sysname; // Operating system name (e.g. Linux).
+- std::string nodename; // Network name of this machine.
+- std::string release; // Release level of the operating system.
+- std::string version; // Version level of the operating system.
+- std::string machine; // Machine hardware platform.
+-};
+-
+-
+-// Return the system information.
+-inline Try<UTSInfo> uname()
+-{
+- struct utsname name;
+-
+- if (::uname(&name) < 0) {
+- return ErrnoError();
+- }
+-
+- UTSInfo info;
+- info.sysname = name.sysname;
+- info.nodename = name.nodename;
+- info.release = name.release;
+- info.version = name.version;
+- info.machine = name.machine;
+- return info;
+-}
+-
+-
+-// Return the operating system name (e.g. Linux).
+-inline Try<std::string> sysname()
+-{
+- Try<UTSInfo> info = uname();
+- if (info.isError()) {
+- return Error(info.error());
+- }
+-
+- return info.get().sysname;
+-}
+-
+-
+-// The OS release level.
+-struct Release
+-{
+- int version;
+- int major;
+- int minor;
+-};
+-
+-
+-// Return the OS release numbers.
+-inline Try<Release> release()
+-{
+- Try<UTSInfo> info = uname();
+- if (info.isError()) {
+- return Error(info.error());
+- }
+-
+- Release r;
+- if (::sscanf(
+- info.get().release.c_str(),
+- "%d.%d.%d",
+- &r.version,
+- &r.major,
+- &r.minor) != 3) {
+- return Error("Failed to parse: " + info.get().release);
+- }
+-
+- return r;
+-}
+-
+-
+-inline Try<std::list<Process> > processes()
+-{
+- const Try<std::set<pid_t> >& pids = os::pids();
+-
+- if (pids.isError()) {
+- return Error(pids.error());
+- }
+-
+- std::list<Process> result;
+- foreach (pid_t pid, pids.get()) {
+- const Result<Process>& process = os::process(pid);
+-
+- // Ignore any processes that disappear.
+- if (process.isSome()) {
+- result.push_back(process.get());
+- }
+- }
+- return result;
+-}
+-
+-
+-inline Option<Process> process(
+- pid_t pid,
+- const std::list<Process>& processes)
+-{
+- foreach (const Process& process, processes) {
+- if (process.pid == pid) {
+- return process;
+- }
+- }
+- return None();
+-}
+-
+-
+-inline std::set<pid_t> children(
+- pid_t pid,
+- const std::list<Process>& processes,
+- bool recursive = true)
+-{
+- // Perform a breadth first search for descendants.
+- std::set<pid_t> descendants;
+- std::queue<pid_t> parents;
+- parents.push(pid);
+-
+- do {
+- pid_t parent = parents.front();
+- parents.pop();
+-
+- // Search for children of parent.
+- foreach (const Process& process, processes) {
+- if (process.parent == parent) {
+- // Have we seen this child yet?
+- if (descendants.insert(process.pid).second) {
+- parents.push(process.pid);
+- }
+- }
+- }
+- } while (recursive && !parents.empty());
+-
+- return descendants;
+-}
+-
+-
+-inline Try<std::set<pid_t> > children(pid_t pid, bool recursive = true)
+-{
+- const Try<std::list<Process> >& processes = os::processes();
+-
+- if (processes.isError()) {
+- return Error(processes.error());
+- }
+-
+- return children(pid, processes.get(), recursive);
+-}
+-
+-
+-// Overload of os::pids for filtering by groups and sessions.
+-// A group / session id of 0 will fitler on the group / session ID
+-// of the calling process.
+-inline Try<std::set<pid_t> > pids(Option<pid_t> group, Option<pid_t> session)
+-{
+- if (group.isNone() && session.isNone()) {
+- return os::pids();
+- } else if (group.isSome() && group.get() < 0) {
+- return Error("Invalid group");
+- } else if (session.isSome() && session.get() < 0) {
+- return Error("Invalid session");
+- }
+-
+- const Try<std::list<Process> >& processes = os::processes();
+-
+- if (processes.isError()) {
+- return Error(processes.error());
+- }
+-
+- // Obtain the calling process group / session ID when 0 is provided.
+- if (group.isSome() && group.get() == 0) {
+- group = getpgid(0);
+- }
+- if (session.isSome() && session.get() == 0) {
+- session = getsid(0);
+- }
+-
+- std::set<pid_t> result;
+- foreach (const Process& process, processes.get()) {
+- // Group AND Session (intersection).
+- if (group.isSome() && session.isSome()) {
+- if (group.get() == process.group &&
+- process.session.isSome() &&
+- session.get() == process.session.get()) {
+- result.insert(process.pid);
+- }
+- } else if (group.isSome() && group.get() == process.group) {
+- result.insert(process.pid);
+- } else if (session.isSome() && process.session.isSome() &&
+- session.get() == process.session.get()) {
+- result.insert(process.pid);
+- }
+- }
+-
+- return result;
+-}
+-
+-} // namespace os {
+-
+-#endif // __STOUT_OS_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/os/exists.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/os/exists.hpp
+deleted file mode 100644
+index 0b30dbe..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/os/exists.hpp
++++ /dev/null
+@@ -1,21 +0,0 @@
+-#ifndef __STOUT_OS_EXISTS_HPP__
+-#define __STOUT_OS_EXISTS_HPP__
+-
+-#include <sys/stat.h>
+-
+-#include <string>
+-
+-namespace os {
+-
+-inline bool exists(const std::string& path)
+-{
+- struct stat s;
+- if (::lstat(path.c_str(), &s) < 0) {
+- return false;
+- }
+- return true;
+-}
+-
+-} // namespace os {
+-
+-#endif // __STOUT_OS_EXISTS_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/os/fork.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/os/fork.hpp
+deleted file mode 100644
+index 838a5fe..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/os/fork.hpp
++++ /dev/null
+@@ -1,433 +0,0 @@
+-#ifndef __STOUT_OS_FORK_HPP__
+-#define __STOUT_OS_FORK_HPP__
+-
+-#include <fcntl.h>
+-#include <unistd.h>
+-
+-#include <sys/mman.h>
+-#include <sys/types.h>
+-#include <sys/wait.h>
+-
+-#include <list>
+-#include <set>
+-#include <string>
+-
+-#include <tr1/memory>
+-
+-#include <stout/error.hpp>
+-#include <stout/exit.hpp>
+-#include <stout/foreach.hpp>
+-#include <stout/option.hpp>
+-#include <stout/os.hpp>
+-#include <stout/stringify.hpp>
+-#include <stout/try.hpp>
+-
+-#include <stout/os/process.hpp>
+-
+-// Abstractions around forking process trees. You can declare a
+-// process tree "template" using 'Fork', 'Exec', and 'Wait'. For
+-// example, to describe a simple "fork/exec" you can do:
+-//
+-// Fork f = Fork(Exec("sleep 10));
+-//
+-// The command passed to an 'Exec' is run via 'sh -c'. You can
+-// construct more complicated templates via nesting, for example:
+-//
+-// Fork f =
+-// Fork(None(),
+-// Fork(Exec("echo 'grandchild 1'")),
+-// Fork(None(),
+-// Fork(Exec("echo 'great-grandchild'")),
+-// Exec("echo 'grandchild 2'"))
+-// Exec("echo 'child'"));
+-//
+-// Note that the first argument to 'Fork' here is an optional function
+-// that can be invoked before forking any more children or executing a
+-// command. THIS FUNCTION SHOULD BE ASYNC SIGNAL SAFE.
+-//
+-// To wait for children, you can use 'Wait' instead of 'Exec', for
+-// example:
+-//
+-// Fork f =
+-// Fork(None(),
+-// Fork(Exec("echo 'grandchild 1'")),
+-// Fork(Exec("echo 'grandchild 2'")),
+-// Wait());
+-//
+-// You can also omit either an 'Exec' or a 'Wait' and the forked
+-// process will just 'exit(0)'. For example, the following will cause
+-// to processes to get reparented by 'init'.
+-//
+-// Fork f =
+-// Fork(None(),
+-// Fork(Exec("echo 'grandchild 1'")),
+-// Fork(Exec("echo 'grandchild 2'")));
+-//
+-// A template can be instantiated by invoking the 'Fork' as a
+-// functor. For example, using any of the templates above we can do:
+-//
+-// Try<ProcessTree> tree = f();
+-//
+-// It's important to note that the process tree returned represents
+-// the instant in time after the forking has completed but before
+-// 'Exec', 'Wait' or 'exit(0)' has occured (i.e., the process tree
+-// will be complete).
+-
+-namespace os {
+-
+-// Forward declaration.
+-inline Result<Process> process(pid_t);
+-
+-
+-struct Exec
+-{
+- Exec(const std::string& _command)
+- : command(_command) {}
+-
+- const std::string command;
+-};
+-
+-
+-struct Wait {};
+-
+-
+-struct Fork
+-{
+- // -+- parent
+- Fork(const Option<void(*)(void)>& _function,
+- const Exec& _exec)
+- : function(_function),
+- exec(_exec) {}
+-
+- Fork(const Exec& _exec) : exec(_exec) {}
+-
+- // -+- parent
+- // \--- child
+- Fork(const Option<void(*)(void)>& _function,
+- const Fork& fork1)
+- : function(_function)
+- {
+- children.push_back(fork1);
+- }
+-
+- Fork(const Option<void(*)(void)>& _function,
+- const Fork& fork1,
+- const Exec& _exec)
+- : function(_function),
+- exec(_exec)
+- {
+- children.push_back(fork1);
+- }
+-
+- Fork(const Option<void(*)(void)>& _function,
+- const Fork& fork1,
+- const Wait& _wait)
+- : function(_function),
+- wait(_wait)
+- {
+- children.push_back(fork1);
+- }
+-
+- // -+- parent
+- // |--- child
+- // \--- child
+- Fork(const Option<void(*)(void)>& _function,
+- const Fork& fork1,
+- const Fork& fork2)
+- : function(_function)
+- {
+- children.push_back(fork1);
+- children.push_back(fork2);
+- }
+-
+- Fork(const Option<void(*)(void)>& _function,
+- const Fork& fork1,
+- const Fork& fork2,
+- const Exec& _exec)
+- : function(_function),
+- exec(_exec)
+- {
+- children.push_back(fork1);
+- children.push_back(fork2);
+- }
+-
+- Fork(const Option<void(*)(void)>& _function,
+- const Fork& fork1,
+- const Fork& fork2,
+- const Wait& _wait)
+- : function(_function),
+- wait(_wait)
+- {
+- children.push_back(fork1);
+- children.push_back(fork2);
+- }
+-
+- // -+- parent
+- // |--- child
+- // |--- child
+- // \--- child
+- Fork(const Option<void(*)(void)>& _function,
+- const Fork& fork1,
+- const Fork& fork2,
+- const Fork& fork3)
+- : function(_function)
+- {
+- children.push_back(fork1);
+- children.push_back(fork2);
+- children.push_back(fork3);
+- }
+-
+- Fork(const Option<void(*)(void)>& _function,
+- const Fork& fork1,
+- const Fork& fork2,
+- const Fork& fork3,
+- const Exec& _exec)
+- : function(_function),
+- exec(_exec)
+- {
+- children.push_back(fork1);
+- children.push_back(fork2);
+- children.push_back(fork3);
+- }
+-
+- Fork(const Option<void(*)(void)>& _function,
+- const Fork& fork1,
+- const Fork& fork2,
+- const Fork& fork3,
+- const Wait& _wait)
+- : function(_function),
+- wait(_wait)
+- {
+- children.push_back(fork1);
+- children.push_back(fork2);
+- children.push_back(fork3);
+- }
+-
+-private:
+- // Represents the "tree" of descendants where each node has a
+- // pointer (into shared memory) from which we can read the
+- // descendants process information as well as a vector of children.
+- struct Tree
+- {
+- // NOTE: This struct is stored in shared memory and thus cannot
+- // hold any pointers to heap allocated memory.
+- struct Memory {
+- pid_t pid;
+- pid_t parent;
+- pid_t group;
+- pid_t session;
+-
+- bool set; // Has this been initialized?
+- };
+-
+- std::tr1::shared_ptr<Memory> memory;
+- std::vector<Tree> children;
+- };
+-
+- // We use shared memory to "share" the pids of forked descendants.
+- // The benefit of shared memory over pipes is that each forked
+- // process can read its descendants' pids leading to a simpler
+- // implementation (with pipes, only one reader can ever read the
+- // value from the pipe, forcing much more complicated coordination).
+- //
+- // Shared memory works like a file (in memory) that gets deleted by
+- // "unlinking" it, but it won't get completely deleted until all
+- // open file descriptors referencing it have been closed. Each
+- // forked process has the shared memory mapped into it as well as an
+- // open file descriptor, both of which should get cleaned up
+- // automagically when the process exits, but we use a special
+- // "deleter" (in combination with shared_ptr) in order to clean this
+- // stuff up when we are actually finished using the shared memory.
+- struct SharedMemoryDeleter
+- {
+- SharedMemoryDeleter(int _fd) : fd(_fd) {}
+-
+- void operator () (Tree::Memory* process) const
+- {
+- if (munmap(process, sizeof(Tree::Memory)) == -1) {
+- perror("Failed to unmap memory");
+- abort();
+- }
+- if (::close(fd) == -1) {
+- perror("Failed to close shared memory file descriptor");
+- abort();
+- }
+- }
+-
+- const int fd;
+- };
+-
+- // Constructs a Tree (see above) from this fork template.
+- Try<Tree> prepare() const
+- {
+- static int forks = 0;
+-
+- // Each "instance" of an instantiated Fork needs a unique name for
+- // creating shared memory.
+- int instance = __sync_fetch_and_add(&forks, 1);
+-
+- std::string name =
+- "/stout-forks-" + stringify(getpid()) + stringify(instance);
+-
+- int fd = shm_open(name.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
+-
+- if (fd == -1) {
+- return ErrnoError("Failed to open a shared memory object");
+- }
+-
+- if (ftruncate(fd, sizeof(Tree::Memory)) == -1) {
+- return ErrnoError("Failed to set size of shared memory object");
+- }
+-
+- void* memory = mmap(
+- NULL,
+- sizeof(Tree::Memory),
+- PROT_READ | PROT_WRITE, MAP_SHARED,
+- fd,
+- 0);
+-
+- if (memory == MAP_FAILED) {
+- return ErrnoError("Failed to map shared memory object");
+- }
+-
+- if (shm_unlink(name.c_str()) == -1) {
+- return ErrnoError("Failed to unlink shared memory object");
+- }
+-
+- SharedMemoryDeleter deleter(fd);
+-
+- Tree tree;
+- tree.memory = std::tr1::shared_ptr<Tree::Memory>(
+- (Tree::Memory*) memory, deleter);
+- tree.memory->set = false;
+-
+- for (size_t i = 0; i < children.size(); i++) {
+- Try<Tree> tree_ = children[i].prepare();
+- if (tree_.isError()) {
+- return Error(tree_.error());
+- }
+- tree.children.push_back(tree_.get());
+- }
+-
+- return tree;
+- }
+-
+- // Performs the fork, executes the function, recursively
+- // instantiates any children, and then executes/waits/exits.
+- pid_t instantiate(const Tree& tree) const
+- {
+- pid_t pid = ::fork();
+- if (pid > 0) {
+- return pid;
+- }
+-
+- // Set the basic process information.
+- Tree::Memory process;
+- process.pid = getpid();
+- process.parent = getppid();
+- process.group = getpgid(0);
+- process.session = getsid(0);
+- process.set = true;
+-
+- // Copy it into shared memory.
+- memcpy(tree.memory.get(), &process, sizeof(Tree::Memory));
+-
+- // Execute the function, if any.
+- if (function.isSome()) {
+- function.get()();
+- }
+-
+- // Fork the children, if any.
+- CHECK(children.size() == tree.children.size());
+- std::set<pid_t> pids;
+- for (size_t i = 0; i < children.size(); i++) {
+- pids.insert(children[i].instantiate(tree.children[i]));
+- }
+-
+- // Execute or wait.
+- if (exec.isSome()) {
+- // Execute the command (via '/bin/sh -c command').
+- const char* command = exec.get().command.c_str();
+- execl("/bin/sh", "sh", "-c", command, (char*) NULL);
+- EXIT(1) << "Failed to execute '" << command << "': " << strerror(errno);
+- } else if (wait.isSome()) {
+- foreach (pid_t pid, pids) {
+- // TODO(benh): Check for signal interruption or other errors.
+- waitpid(pid, NULL, 0);
+- }
+- }
+-
+- exit(0);
+- return -1;
+- }
+-
+- // Waits for all of the descendant processes in the tree to update
+- // their pids and constructs a ProcessTree using the Tree::Memory
+- // information from shared memory.
+- static Try<ProcessTree> coordinate(const Tree& tree)
+- {
+- // Wait for the forked process.
+- // TODO(benh): Don't wait forever?
+- while (!tree.memory->set) {
+- // Make sure we don't keep reading the value from a register.
+- __sync_synchronize();
+- }
+-
+- // All processes in the returned ProcessTree will have the
+- // command-line of the top level process, since we construct the
+- // tree using post-fork pre-exec information. So, we'll grab the
+- // command of the current process here.
+- Result<Process> self = os::process(getpid());
+-
+- Process process = Process(
+- tree.memory->pid,
+- tree.memory->parent,
+- tree.memory->group,
+- tree.memory->session,
+- None(),
+- None(),
+- None(),
+- self.isSome() ? self.get().command : "",
+- false);
+-
+- std::list<ProcessTree> children;
+- for (size_t i = 0; i < tree.children.size(); i++) {
+- Try<ProcessTree> child = coordinate(tree.children[i]);
+- if (child.isError()) {
+- return Error(child.error());
+- }
+- children.push_back(child.get());
+- }
+-
+- return ProcessTree(process, children);
+- }
+-
+-public:
+- // Prepares and instantiates the process tree.
+- Try<ProcessTree> operator () () const
+- {
+- Try<Tree> tree = prepare();
+-
+- if (tree.isError()) {
+- return Error(tree.error());
+- }
+-
+- Try<pid_t> pid = instantiate(tree.get());
+-
+- if (pid.isError()) {
+- return Error(pid.error());
+- }
+-
+- return coordinate(tree.get());
+- }
+-
+-private:
+- Option<void(*)(void)> function;
+- Option<const Exec> exec;
+- Option<const Wait> wait;
+- std::vector<Fork> children;
+-};
+-
+-} // namespace os {
+-
+-#endif // __STOUT_OS_FORK_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/os/killtree.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/os/killtree.hpp
+deleted file mode 100644
+index 25e9937..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/os/killtree.hpp
++++ /dev/null
+@@ -1,186 +0,0 @@
+-#ifndef __STOUT_OS_KILLTREE_HPP__
+-#define __STOUT_OS_KILLTREE_HPP__
+-
+-#include <dirent.h>
+-#include <stdlib.h>
+-#include <unistd.h>
+-
+-#include <list>
+-#include <ostream>
+-#include <queue>
+-#include <set>
+-#include <sstream>
+-#include <string>
+-
+-#include <stout/check.hpp>
+-#include <stout/os.hpp>
+-#include <stout/stringify.hpp>
+-
+-#include <stout/os/pstree.hpp>
+-
+-namespace os {
+-
+-// Forward declarations from os.hpp.
+-inline std::set<pid_t> children(pid_t, const std::list<Process>&, bool);
+-inline Option<Process> process(pid_t, const std::list<Process>&);
+-
+-
+-// Sends a signal to a process tree rooted at the specified pid.
+-// If groups is true, this also sends the signal to all encountered
+-// process groups.
+-// If sessions is true, this also sends the signal to all encountered
+-// process sessions.
+-// Note that processes of the group and session of the parent of the
+-// root process is not included unless they are part of the root
+-// process tree.
+-// Returns the process trees that were succesfully or unsuccessfully
+-// signaled. Note that the process trees can be stringified.
+-inline Try<std::list<ProcessTree> > killtree(
+- pid_t pid,
+- int signal,
+- bool groups = false,
+- bool sessions = false)
+-{
+- Try<std::list<Process> > processes = os::processes();
+-
+- if (processes.isError()) {
+- return Error(processes.error());
+- }
+-
+- Result<Process> process = os::process(pid, processes.get());
+-
+- if (process.isNone()) {
+- return Error("Failed to find process " + stringify(pid));
+- }
+-
+- struct {
+- std::set<pid_t> pids;
+- std::set<pid_t> groups;
+- std::set<pid_t> sessions;
+- std::list<Process> processes;
+- } visited;
+-
+- // If we are following groups and/or sessions then we try and make
+- // the group and session of the parent process "already visited" so
+- // that we don't kill "up the tree".
+- if (groups || sessions) {
+- Option<Process> parent =
+- os::process(process.get().parent, processes.get());
+-
+- if (parent.isSome()) {
+- if (groups) {
+- visited.groups.insert(parent.get().group);
+- }
+- if (sessions && parent.get().session.isSome()) {
+- visited.sessions.insert(parent.get().session.get());
+- }
+- }
+- }
+-
+- std::queue<pid_t> queue;
+- queue.push(pid);
+-
+- while (!queue.empty()) {
+- pid_t pid = queue.front();
+- queue.pop();
+-
+- if (visited.pids.count(pid) != 0) {
+- continue;
+- }
+-
+- // Make sure this process still exists.
+- process = os::process(pid);
+-
+- if (process.isError()) {
+- return Error(process.error());
+- } else if (process.isNone()) {
+- continue;
+- }
+-
+- // Stop the process to keep it from forking while we are killing
+- // it since a forked child might get re-parented by init and
+- // become impossible to find.
+- kill(pid, SIGSTOP);
+-
+- visited.pids.insert(pid);
+- visited.processes.push_back(process.get());
+-
+- // Now refresh the process list knowing that the current process
+- // can't fork any more children (since it's stopped).
+- processes = os::processes();
+-
+- if (processes.isError()) {
+- return Error(processes.error());
+- }
+-
+- // Enqueue the children for visiting.
+- foreach (pid_t child, os::children(pid, processes.get(), false)) {
+- queue.push(child);
+- }
+-
+- // Now "visit" the group and/or session of the current process.
+- if (groups) {
+- pid_t group = process.get().group;
+- if (visited.groups.count(group) == 0) {
+- foreach (const Process& process, processes.get()) {
+- if (process.group == group) {
+- queue.push(process.pid);
+- }
+- }
+- visited.groups.insert(group);
+- }
+- }
+-
+- // If we do not have a session for the process, it's likely
+- // because the process is a zombie on OS X. This implies it has
+- // not been reaped and thus is located somewhere in the tree we
+- // are trying to kill. Therefore, we should discover it from our
+- // tree traversal, or through its group (which is always present).
+- if (sessions && process.get().session.isSome()) {
+- pid_t session = process.get().session.get();
+- if (visited.sessions.count(session) == 0) {
+- foreach (const Process& process, processes.get()) {
+- if (process.session.isSome() && process.session.get() == session) {
+- queue.push(process.pid);
+- }
+- }
+- visited.sessions.insert(session);
+- }
+- }
+- }
+-
+- // Now that all processes are stopped, we send the signal.
+- foreach (pid_t pid, visited.pids) {
+- kill(pid, signal);
+- }
+-
+- // There is a concern that even though some process is stopped,
+- // sending a signal to any of it's children may cause a SIGCLD to
+- // be delivered to it which wakes it up (or any other signal maybe
+- // delivered). However, from the Open Group standards on "Signal
+- // Concepts":
+- //
+- // "While a process is stopped, any additional signals that are
+- // sent to the process shall not be delivered until the process
+- // is continued, except SIGKILL which always terminates the
+- // receiving process."
+- //
+- // In practice, this is not what has been witnessed. Rather, a
+- // process that has been stopped will respond to SIGTERM, SIGINT,
+- // etc. That being said, we still continue the process below in the
+- // event that it doesn't terminate from the sending signal but it
+- // also doesn't get continued (as per the specifications above).
+-
+- // Try and continue the processes in case the signal is
+- // non-terminating but doesn't continue the process.
+- foreach (pid_t pid, visited.pids) {
+- kill(pid, SIGCONT);
+- }
+-
+- // Return the process trees representing the visited pids.
+- return pstrees(visited.pids, visited.processes);
+-}
+-
+-} // namespace os {
+-
+-#endif // __STOUT_OS_KILLTREE_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/os/linux.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/os/linux.hpp
+deleted file mode 100644
+index 25d5903..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/os/linux.hpp
++++ /dev/null
+@@ -1,87 +0,0 @@
+-#ifndef __STOUT_OS_LINUX_HPP__
+-#define __STOUT_OS_LINUX_HPP__
+-
+-// This file contains Linux-only OS utilities.
+-#ifndef __linux__
+-#error "stout/os/linux.hpp is only available on Linux systems."
+-#endif
+-
+-#include <sys/types.h> // For pid_t.
+-
+-#include <list>
+-#include <queue>
+-#include <set>
+-
+-#include <stout/error.hpp>
+-#include <stout/foreach.hpp>
+-#include <stout/option.hpp>
+-#include <stout/proc.hpp>
+-#include <stout/result.hpp>
+-#include <stout/try.hpp>
+-
+-#include <stout/os/process.hpp>
+-
+-namespace os {
+-
+-inline Result<Process> process(pid_t pid)
+-{
+- // Page size, used for memory accounting.
+- // NOTE: This is more portable than using getpagesize().
+- static const long pageSize = sysconf(_SC_PAGESIZE);
+- if (pageSize <= 0) {
+- return Error("Failed to get sysconf(_SC_PAGESIZE)");
+- }
+-
+- // Number of clock ticks per second, used for cpu accounting.
+- static const long ticks = sysconf(_SC_CLK_TCK);
+- if (ticks <= 0) {
+- return Error("Failed to get sysconf(_SC_CLK_TCK)");
+- }
+-
+- const Result<proc::ProcessStatus>& status = proc::status(pid);
+-
+- if (status.isError()) {
+- return Error(status.error());
+- }
+-
+- if (status.isNone()) {
+- return None();
+- }
+-
+- // There are known bugs with invalid utime / stime values coming
+- // from /proc/<pid>/stat on some Linux systems.
+- // See the following thread for details:
+- // http://mail-archives.apache.org/mod_mbox/incubator-mesos-dev/
+- // 201307.mbox/%3CCA+2n2er-Nemh0CsKLbHRkaHd=YCrNt17NLUPM2=TtEfsKOw4
+- // Rg at mail.gmail.com%3E
+- // These are similar reports:
+- // http://lkml.indiana.edu/hypermail/linux/kernel/1207.1/01388.html
+- // https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1023214
+- Try<Duration> utime = Duration::create(status.get().utime / (double) ticks);
+- Try<Duration> stime = Duration::create(status.get().stime / (double) ticks);
+-
+- // The command line from 'status.get().comm' is only "arg0" from
+- // "argv" (i.e., the canonical executable name). To get the entire
+- // command line we grab '/proc/[pid]/cmdline'.
+- Result<std::string> cmdline = proc::cmdline(pid);
+-
+- return Process(status.get().pid,
+- status.get().ppid,
+- status.get().pgrp,
+- status.get().session,
+- Bytes(status.get().rss * pageSize),
+- utime.isSome() ? utime.get() : Option<Duration>::none(),
+- stime.isSome() ? stime.get() : Option<Duration>::none(),
+- cmdline.isSome() ? cmdline.get() : status.get().comm,
+- status.get().state == 'Z');
+-}
+-
+-
+-inline Try<std::set<pid_t> > pids()
+-{
+- return proc::pids();
+-}
+-
+-} // namespace os {
+-
+-#endif // __STOUT_OS_LINUX_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/os/ls.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/os/ls.hpp
+deleted file mode 100644
+index 7637a0d..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/os/ls.hpp
++++ /dev/null
+@@ -1,66 +0,0 @@
+-#ifndef __STOUT_OS_LS_HPP__
+-#define __STOUT_OS_LS_HPP__
+-
+-#include <dirent.h>
+-#include <stdlib.h>
+-#include <unistd.h>
+-
+-#include <list>
+-#include <string>
+-
+-namespace os {
+-
+-// TODO(bmahler): Wrap this with a Try.
+-inline std::list<std::string> ls(const std::string& directory)
+-{
+- DIR* dir = opendir(directory.c_str());
+-
+- if (dir == NULL) {
+- return std::list<std::string>();
+- }
+-
+- // Calculate the size for a "directory entry".
+- long name_max = fpathconf(dirfd(dir), _PC_NAME_MAX);
+-
+- // If we don't get a valid size, check NAME_MAX, but fall back on
+- // 255 in the worst case ... Danger, Will Robinson!
+- if (name_max == -1) {
+- name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
+- }
+-
+- size_t name_end = (size_t) offsetof(dirent, d_name) + name_max + 1;
+-
+- size_t size = (name_end > sizeof(dirent) ? name_end : sizeof(dirent));
+-
+- dirent* temp = (dirent*) malloc(size);
+-
+- if (temp == NULL) {
+- free(temp);
+- closedir(dir);
+- return std::list<std::string>();
+- }
+-
+- std::list<std::string> result;
+- struct dirent* entry;
+- int error;
+-
+- while ((error = readdir_r(dir, temp, &entry)) == 0 && entry != NULL) {
+- if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
+- continue;
+- }
+- result.push_back(entry->d_name);
+- }
+-
+- free(temp);
+- closedir(dir);
+-
+- if (error != 0) {
+- return std::list<std::string>();
+- }
+-
+- return result;
+-}
+-
+-} // namespace os {
+-
+-#endif // __STOUT_OS_LS_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/os/osx.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/os/osx.hpp
+deleted file mode 100644
+index 7d02566..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/os/osx.hpp
++++ /dev/null
+@@ -1,165 +0,0 @@
+-#ifndef __STOUT_OS_OSX_HPP__
+-#define __STOUT_OS_OSX_HPP__
+-
+-// This file contains OSX-only OS utilities.
+-#ifndef __APPLE__
+-#error "stout/os/osx.hpp is only available on OSX systems."
+-#endif
+-
+-#include <libproc.h>
+-
+-#include <sys/sysctl.h>
+-#include <sys/types.h> // For pid_t.
+-
+-#include <queue>
+-#include <set>
+-
+-#include <stout/error.hpp>
+-#include <stout/none.hpp>
+-#include <stout/strings.hpp>
+-
+-#include <stout/os/process.hpp>
+-#include <stout/os/sysctl.hpp>
+-
+-namespace os {
+-
+-inline Result<Process> process(pid_t pid)
+-{
+- const Try<std::vector<kinfo_proc> >& processes =
+- os::sysctl(CTL_KERN, KERN_PROC, KERN_PROC_PID, pid).table(1);
+-
+- if (processes.isError()) {
+- return Error("Failed to get process via sysctl: " + processes.error());
+- } else if (processes.get().size() != 1) {
+- return None();
+- }
+-
+- const kinfo_proc process = processes.get()[0];
+-
+- // The command line from 'process.kp_proc.p_comm' only includes the
+- // first 16 characters from "arg0" (i.e., the canonical executable
+- // name). We can try to get "argv" via some sysctl magic. This first
+- // requires determining "argc" via KERN_PROCARGS2 followed by the
+- // actual arguments via KERN_PROCARGS. This is still insufficient
+- // with insufficient privilege (e.g., not being root). If we were
+- // only interested in the "executable path" (i.e., the first
+- // argument to 'exec' but none of the arguments) we could use
+- // proc_pidpath() instead.
+- Option<std::string> command = None();
+-
+-#ifdef KERN_PROCARGS2
+- // Looking at the source code of XNU (the Darwin kernel for OS X:
+- // www.opensource.apple.com/source/xnu/xnu-1699.24.23/bsd/kern/kern_sysctl.c),
+- // it appears as though KERN_PROCARGS2 writes 'argc' as the first
+- // word of the returned bytes.
+- Try<std::string> args = os::sysctl(CTL_KERN, KERN_PROCARGS2, pid).string();
+-
+- if (args.isSome()) {
+- int argc = *((int*) args.get().data());
+-
+- if (argc > 0) {
+- // Now grab the arguments.
+- args = os::sysctl(CTL_KERN, KERN_PROCARGS, pid).string();
+-
+- if (args.isSome()) {
+- // At this point 'args' contains the parameters to 'exec'
+- // delimited by null bytes, i.e., "executable path", then
+- // "arg0" (the canonical executable name), then "arg1", then
+- // "arg2", etc. Sometimes there are no arguments (argc = 1) so
+- // all we care about is the "executable path", but when there
+- // are arguments we grab "arg0" and on assuming that "arg0"
+- // really is the canonical executable name.
+-
+- // Tokenize the args by the null byte ('\0').
+- std::vector<std::string> tokens =
+- strings::tokenize(args.get(), std::string(1, '\0'));
+-
+- if (!tokens.empty()) {
+- if (argc == 1) {
+- // When there are no arguments, all we care about is the
+- // "executable path".
+- command = tokens[0];
+- } else if (argc > 1) {
+- // When there are arguments, we skip the "executable path"
+- // and just grab "arg0" -> "argN", assuming "arg0" is the
+- // canonical executable name. In the case that we didn't
+- // get enough tokens back from KERN_PROCARGS the following
+- // code will end up just keeping 'command' None (i.e.,
+- // tokens.size() will be <= 0).
+- tokens.erase(tokens.begin()); // Remove path.
+- tokens.erase(tokens.begin() + argc, tokens.end());
+- if (tokens.size() > 0) {
+- command = strings::join(" ", tokens);
+- }
+- }
+- }
+- }
+- }
+- }
+-#endif
+-
+- // We also use proc_pidinfo() to get memory and CPU usage.
+- // NOTE: There are several pitfalls to using proc_pidinfo().
+- // In particular:
+- // -This will not work for many root processes.
+- // -This may not work for processes owned by other users.
+- // -However, this always works for processes owned by the same user.
+- // This beats using task_for_pid(), which only works for the same pid.
+- // For further discussion around these issues,
+- // see: http://code.google.com/p/psutil/issues/detail?id=297
+- proc_taskinfo task;
+- int size = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &task, sizeof(task));
+-
+- // It appears that zombie processes on OS X do not have sessions and
+- // result in ESRCH.
+- int session = getsid(pid);
+-
+- if (size != sizeof(task)) {
+- return Process(process.kp_proc.p_pid,
+- process.kp_eproc.e_ppid,
+- process.kp_eproc.e_pgid,
+- session > 0 ? session : Option<pid_t>::none(),
+- None(),
+- None(),
+- None(),
+- command.get(std::string(process.kp_proc.p_comm)),
+- process.kp_proc.p_stat & SZOMB);
+- } else {
+- return Process(process.kp_proc.p_pid,
+- process.kp_eproc.e_ppid,
+- process.kp_eproc.e_pgid,
+- session > 0 ? session : Option<pid_t>::none(),
+- Bytes(task.pti_resident_size),
+- Nanoseconds(task.pti_total_user),
+- Nanoseconds(task.pti_total_system),
+- command.get(std::string(process.kp_proc.p_comm)),
+- process.kp_proc.p_stat & SZOMB);
+- }
+-}
+-
+-
+-inline Try<std::set<pid_t> > pids()
+-{
+- const Try<int>& maxproc = os::sysctl(CTL_KERN, KERN_MAXPROC).integer();
+-
+- if (maxproc.isError()) {
+- return Error(maxproc.error());
+- }
+-
+- const Try<std::vector<kinfo_proc> >& processes =
+- os::sysctl(CTL_KERN, KERN_PROC, KERN_PROC_ALL).table(maxproc.get());
+-
+- if (processes.isError()) {
+- return Error(processes.error());
+- }
+-
+- std::set<pid_t> result;
+- foreach (const kinfo_proc& process, processes.get()) {
+- result.insert(process.kp_proc.p_pid);
+- }
+- return result;
+-}
+-
+-} // namespace os {
+-
+-#endif // __STOUT_OS_OSX_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/os/process.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/os/process.hpp
+deleted file mode 100644
+index d754601..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/os/process.hpp
++++ /dev/null
+@@ -1,166 +0,0 @@
+-#ifndef __STOUT_OS_PROCESS_HPP__
+-#define __STOUT_OS_PROCESS_HPP__
+-
+-#include <sys/types.h> // For pid_t.
+-
+-#include <list>
+-#include <ostream>
+-#include <sstream>
+-#include <string>
+-
+-#include <stout/bytes.hpp>
+-#include <stout/duration.hpp>
+-#include <stout/none.hpp>
+-#include <stout/option.hpp>
+-#include <stout/strings.hpp>
+-
+-namespace os {
+-
+-struct Process
+-{
+- Process(pid_t _pid,
+- pid_t _parent,
+- pid_t _group,
+- const Option<pid_t>& _session,
+- const Option<Bytes>& _rss,
+- const Option<Duration>& _utime,
+- const Option<Duration>& _stime,
+- const std::string& _command,
+- bool _zombie)
+- : pid(_pid),
+- parent(_parent),
+- group(_group),
+- session(_session),
+- rss(_rss),
+- utime(_utime),
+- stime(_stime),
+- command(_command),
+- zombie(_zombie) {}
+-
+- const pid_t pid;
+- const pid_t parent;
+- const pid_t group;
+- const Option<pid_t> session;
+- const Option<Bytes> rss;
+- const Option<Duration> utime;
+- const Option<Duration> stime;
+- const std::string command;
+- const bool zombie;
+-
+- // TODO(bmahler): Add additional data as needed.
+-
+- bool operator < (const Process& p) const { return pid < p.pid; }
+- bool operator <= (const Process& p) const { return pid <= p.pid; }
+- bool operator > (const Process& p) const { return pid > p.pid; }
+- bool operator >= (const Process& p) const { return pid >= p.pid; }
+- bool operator == (const Process& p) const { return pid == p.pid; }
+- bool operator != (const Process& p) const { return pid != p.pid; }
+-};
+-
+-
+-class ProcessTree
+-{
+-public:
+- // Returns a process subtree rooted at the specified PID, or none if
+- // the specified pid could not be found in this process tree.
+- Option<ProcessTree> find(pid_t pid) const
+- {
+- if (process.pid == pid) {
+- return *this;
+- }
+-
+- foreach (const ProcessTree& tree, children) {
+- Option<ProcessTree> option = tree.find(pid);
+- if (option.isSome()) {
+- return option;
+- }
+- }
+-
+- return None();
+- }
+-
+- // Checks if the specified pid is contained in this process tree.
+- bool contains(pid_t pid) const
+- {
+- return find(pid).isSome();
+- }
+-
+- operator Process () const
+- {
+- return process;
+- }
+-
+- operator pid_t () const
+- {
+- return process.pid;
+- }
+-
+- const Process process;
+- const std::list<ProcessTree> children;
+-
+-private:
+- friend struct Fork;
+- friend Try<ProcessTree> pstree(pid_t, const std::list<Process>&);
+-
+- ProcessTree(
+- const Process& _process,
+- const std::list<ProcessTree>& _children)
+- : process(_process),
+- children(_children) {}
+-};
+-
+-
+-inline std::ostream& operator << (
+- std::ostream& stream,
+- const ProcessTree& tree)
+-{
+- if (tree.children.empty()) {
+- stream << "--- " << tree.process.pid << " ";
+- if (tree.process.zombie) {
+- stream << "(" << tree.process.command << ")";
+- } else {
+- stream << tree.process.command;
+- }
+- } else {
+- stream << "-+- " << tree.process.pid << " ";
+- if (tree.process.zombie) {
+- stream << "(" << tree.process.command << ")";
+- } else {
+- stream << tree.process.command;
+- }
+- size_t size = tree.children.size();
+- foreach (const ProcessTree& child, tree.children) {
+- std::ostringstream out;
+- out << child;
+- stream << "\n";
+- if (--size != 0) {
+- stream << " |" << strings::replace(out.str(), "\n", "\n |");
+- } else {
+- stream << " \\" << strings::replace(out.str(), "\n", "\n ");
+- }
+- }
+- }
+- return stream;
+-}
+-
+-} // namespace os {
+-
+-
+-// An overload of stringify for printing a list of process trees
+-// (since printing a process tree is rather particular).
+-inline std::string stringify(const std::list<os::ProcessTree>& list)
+-{
+- std::ostringstream out;
+- out << "[ " << std::endl;
+- std::list<os::ProcessTree>::const_iterator iterator = list.begin();
+- while (iterator != list.end()) {
+- out << stringify(*iterator);
+- if (++iterator != list.end()) {
+- out << std::endl << std::endl;
+- }
+- }
+- out << std::endl << "]";
+- return out.str();
+-}
+-
+-#endif // __STOUT_OS_PROCESS_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/os/pstree.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/os/pstree.hpp
+deleted file mode 100644
+index 4637e68..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/os/pstree.hpp
++++ /dev/null
+@@ -1,119 +0,0 @@
+-#ifndef __STOUT_OS_PSTREE_HPP__
+-#define __STOUT_OS_PSTREE_HPP__
+-
+-#include <list>
+-#include <set>
+-
+-#include <stout/error.hpp>
+-#include <stout/foreach.hpp>
+-#include <stout/none.hpp>
+-#include <stout/option.hpp>
+-#include <stout/os.hpp>
+-#include <stout/stringify.hpp>
+-#include <stout/try.hpp>
+-
+-#include <stout/os/process.hpp>
+-
+-namespace os {
+-
+-// Forward declaration.
+-inline Try<std::list<Process> > processes();
+-
+-
+-// Returns a process tree rooted at the specified pid using the
+-// specified list of processes (or an error if one occurs).
+-inline Try<ProcessTree> pstree(
+- pid_t pid,
+- const std::list<Process>& processes)
+-{
+- std::list<ProcessTree> children;
+- foreach (const Process& process, processes) {
+- if (process.parent == pid) {
+- Try<ProcessTree> tree = pstree(process.pid, processes);
+- if (tree.isError()) {
+- return Error(tree.error());
+- }
+- children.push_back(tree.get());
+- }
+- }
+-
+- foreach (const Process& process, processes) {
+- if (process.pid == pid) {
+- return ProcessTree(process, children);
+- }
+- }
+-
+- return Error("No process found at " + stringify(pid));
+-}
+-
+-
+-// Returns a process tree for the specified pid (or all processes if
+-// pid is none or the current process if pid is 0).
+-inline Try<ProcessTree> pstree(Option<pid_t> pid = None())
+-{
+- if (pid.isNone()) {
+- pid = 1;
+- } else if (pid.get() == 0) {
+- pid = getpid();
+- }
+-
+- const Try<std::list<Process> >& processes = os::processes();
+-
+- if (processes.isError()) {
+- return Error(processes.error());
+- }
+-
+- return pstree(pid.get(), processes.get());
+-}
+-
+-
+-// Returns the minimum list of process trees that include all of the
+-// specified pids using the specified list of processes.
+-inline Try<std::list<ProcessTree> > pstrees(
+- const std::set<pid_t>& pids,
+- const std::list<Process>& processes)
+-{
+- std::list<ProcessTree> trees;
+-
+- foreach (pid_t pid, pids) {
+- // First, check if the pid is already connected to one of the
+- // process trees we've constructed.
+- bool disconnected = true;
+- foreach (const ProcessTree& tree, trees) {
+- if (tree.contains(pid)) {
+- disconnected = false;
+- break;
+- }
+- }
+-
+- if (disconnected) {
+- Try<ProcessTree> tree = pstree(pid, processes);
+- if (tree.isError()) {
+- return Error(tree.error());
+- }
+-
+- // Now see if any of the existing process trees are actually
+- // contained within the process tree we just created and only
+- // includ the disjoint process trees.
+- // C++11:
+- // trees = trees.filter([] (const ProcessTree& t) {
+- // return tree.get().contains(t);
+- // });
+- std::list<ProcessTree> trees_ = trees;
+- trees.clear();
+- foreach (const ProcessTree& t, trees_) {
+- if (tree.get().contains(t.process.pid)) {
+- continue;
+- }
+- trees.push_back(t);
+- }
+- trees.push_back(tree.get());
+- }
+- }
+-
+- return trees;
+-}
+-
+-} // namespace os {
+-
+-#endif // __STOUT_OS_PSTREE_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/os/read.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/os/read.hpp
+deleted file mode 100644
+index 587b7b9..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/os/read.hpp
++++ /dev/null
+@@ -1,97 +0,0 @@
+-#ifndef __STOUT_OS_READ_HPP__
+-#define __STOUT_OS_READ_HPP__
+-
+-#include <stdio.h>
+-#include <unistd.h>
+-
+-#include <stout/error.hpp>
+-#include <stout/try.hpp>
+-
+-namespace os {
+-
+-// Reads 'size' bytes from a file from its current offset.
+-// If EOF is encountered before reading size bytes, then the offset
+-// is restored and none is returned.
+-inline Result<std::string> read(int fd, size_t size)
+-{
+- // Save the current offset.
+- off_t current = lseek(fd, 0, SEEK_CUR);
+- if (current == -1) {
+- return ErrnoError("Failed to lseek to SEEK_CUR");
+- }
+-
+- char* buffer = new char[size];
+- size_t offset = 0;
+-
+- while (offset < size) {
+- ssize_t length = ::read(fd, buffer + offset, size - offset);
+-
+- if (length < 0) {
+- // TODO(bmahler): Handle a non-blocking fd? (EAGAIN, EWOULDBLOCK)
+- if (errno == EINTR) {
+- continue;
+- }
+- // Attempt to restore the original offset.
+- lseek(fd, current, SEEK_SET);
+- delete[] buffer;
+- return ErrnoError();
+- } else if (length == 0) {
+- // Reached EOF before expected! Restore the offset.
+- lseek(fd, current, SEEK_SET);
+- delete[] buffer;
+- return None();
+- }
+-
+- offset += length;
+- }
+-
+- std::string result = std::string(buffer, size);
+- delete[] buffer;
+- return result;
+-}
+-
+-
+-// Returns the contents of the file.
+-inline Try<std::string> read(const std::string& path)
+-{
+- FILE* file = fopen(path.c_str(), "r");
+- if (file == NULL) {
+- return ErrnoError("Failed to open file '" + path + "'");
+- }
+-
+- // Initially the 'line' is NULL and length 0, getline() allocates
+- // ('malloc') a buffer for reading the line.
+- // In subsequent iterations, if the buffer is not large enough to
+- // hold the line, getline() resizes it with 'realloc' and updates
+- // 'line' and 'length' as necessary. See:
+- // - http://pubs.opengroup.org/onlinepubs/9699919799/functions/getline.html
+- // - http://man7.org/linux/man-pages/man3/getline.3.html
+- std::string result;
+- char* line = NULL;
+- size_t length = 0;
+- ssize_t read;
+-
+- while ((read = getline(&line, &length, file)) != -1) {
+- result.append(line, read);
+- }
+-
+- // getline() requires the line buffer to be freed by the caller.
+- free(line);
+-
+- if (ferror(file)) {
+- ErrnoError error;
+- // NOTE: We ignore the error from fclose(). This is because
+- // users calling this function are interested in the return value
+- // of read(). Also an unsuccessful fclose() does not affect the
+- // read.
+- fclose(file);
+- return error;
+- }
+-
+- fclose(file);
+- return result;
+-}
+-
+-} // namespace os {
+-
+-#endif // __STOUT_OS_READ_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/os/sendfile.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/os/sendfile.hpp
+deleted file mode 100644
+index 668e4da..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/os/sendfile.hpp
++++ /dev/null
+@@ -1,55 +0,0 @@
+-#ifndef __STOUT_OS_SENDFILE_HPP__
+-#define __STOUT_OS_SENDFILE_HPP__
+-
+-#include <errno.h>
+-
+-#ifdef __linux__
+-#include <sys/sendfile.h>
+-#endif // __linux__
+-#ifdef __APPLE__
+-#include <sys/socket.h>
+-#include <sys/types.h>
+-#include <sys/uio.h>
+-#endif // __APPLE__
+-
+-#ifdef __linux__
+-#include <stout/fatal.hpp>
+-#endif // __linux__
+-#include <stout/os/signals.hpp>
+-
+-namespace os {
+-
+-// Returns the amount of bytes written from the input file
+-// descriptor to the output socket. On error, returns -1 and
+-// errno indicates the error.
+-// NOTE: The following limitations exist because of the OS X
+-// implementation of sendfile:
+-// 1. s must be a stream oriented socket descriptor.
+-// 2. fd must be a regular file descriptor.
+-inline ssize_t sendfile(int s, int fd, off_t offset, size_t length)
+-{
+-#ifdef __linux__
+- suppress (SIGPIPE) {
+- // This will set errno to EPIPE if a SIGPIPE occurs.
+- return ::sendfile(s, fd, &offset, length);
+- }
+- fatal("Unreachable statement");
+- return -1;
+-#elif defined __APPLE__
+- // On OS X, sendfile does not need to have SIGPIPE suppressed.
+- off_t _length = static_cast<off_t>(length);
+-
+- if (::sendfile(fd, s, offset, &_length, NULL, 0) < 0) {
+- if (errno == EAGAIN && _length > 0) {
+- return _length;
+- }
+- return -1;
+- }
+-
+- return _length;
+-#endif // __APPLE__
+-}
+-
+-} // namespace os {
+-
+-#endif // __STOUT_OS_SENDFILE_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/os/signals.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/os/signals.hpp
+deleted file mode 100644
+index 215ee55..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/os/signals.hpp
++++ /dev/null
+@@ -1,150 +0,0 @@
+-#ifndef __STOUT_OS_SIGNALS_HPP__
+-#define __STOUT_OS_SIGNALS_HPP__
+-
+-#include <errno.h>
+-#include <pthread.h>
+-#include <signal.h>
+-#include <unistd.h>
+-
+-namespace os {
+-
+-namespace signals {
+-
+-// Returns true iff the signal is pending.
+-inline bool pending(int signal)
+-{
+- sigset_t set;
+- sigemptyset(&set);
+- sigpending(&set);
+- return sigismember(&set, signal);
+-}
+-
+-
+-// Returns true if the signal has been blocked, or false if the
+-// signal was already blocked.
+-inline bool block(int signal)
+-{
+- sigset_t set;
+- sigaddset(&set, signal);
+-
+- sigset_t oldset;
+- sigemptyset(&oldset);
+-
+- // We ignore errors here as the only documented one is
+- // EINVAL due to a bad value of the SIG_* argument.
+- pthread_sigmask(SIG_BLOCK, &set, &oldset);
+-
+- return !sigismember(&oldset, signal);
+-}
+-
+-
+-// Returns true if the signal has been unblocked, or false if the
+-// signal was not previously blocked.
+-inline bool unblock(int signal)
+-{
+- sigset_t set;
+- sigaddset(&set, signal);
+-
+- sigset_t oldset;
+- sigemptyset(&oldset);
+-
+- pthread_sigmask(SIG_UNBLOCK, &set, &oldset);
+-
+- return sigismember(&oldset, signal);
+-}
+-
+-namespace internal {
+-
+-// Suppresses a signal on the current thread for the lifetime of
+-// the Suppressor. The signal *must* be synchronous and delivered
+-// per-thread. The suppression occurs only on the thread of
+-// execution of the Suppressor.
+-struct Suppressor
+-{
+- Suppressor(int _signal)
+- : signal(_signal), pending(false), unblock(false)
+- {
+- // Check to see if the signal is already reported as pending.
+- // If pending, it means the thread already blocks the signal!
+- // Therefore, any new instances of the signal will also be
+- // blocked and merged with the pending one since there is no
+- // queuing for signals.
+- pending = signals::pending(signal);
+-
+- if (!pending) {
+- // Block the signal for this thread only. If already blocked,
+- // there's no need to unblock it.
+- unblock = signals::block(signal);
+- }
+- }
+-
+- ~Suppressor()
+- {
+- // We want to preserve errno when the Suppressor drops out of
+- // scope. Otherwise, one needs to potentially store errno when
+- // using the suppress() macro.
+- int _errno = errno;
+-
+- // If the signal has become pending after we blocked it, we
+- // need to clear it before unblocking it.
+- if (!pending && signals::pending(signal)) {
+- // It is possible that in between having observed the pending
+- // signal with sigpending() and clearing it with sigwait(),
+- // the signal was delivered to another thread before we were
+- // able to clear it here. This can happen if the signal was
+- // generated for the whole process (e.g. a kill was issued).
+- // See 2.4.1 here:
+- // http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html
+- // To handle the above scenario, one can either:
+- // 1. Use sigtimedwait() with a timeout of 0, to ensure we
+- // don't block forever. However, this only works on Linux
+- // and we may still swallow the signal intended for the
+- // process.
+- // 2. After seeing the pending signal, signal ourselves with
+- // pthread_kill prior to calling sigwait(). This can still
+- // swallow the signal intended for the process.
+- // We chose to use the latter technique as it works on all
+- // POSIX systems and is less likely to swallow process signals,
+- // provided the thread signal and process signal are not merged.
+- pthread_kill(pthread_self(), signal);
+-
+- sigset_t mask;
+- sigemptyset(&mask);
+- sigaddset(&mask, signal);
+-
+- int result;
+- do {
+- int _ignored;
+- result = sigwait(&mask, &_ignored);
+- } while (result == -1 && errno == EINTR);
+- }
+-
+- // Unblock the signal (only if we were the ones to block it).
+- if (unblock) {
+- signals::unblock(signal);
+- }
+-
+- // Restore errno.
+- errno = _errno;
+- }
+-
+- // Needed for the suppress() macro.
+- operator bool () { return true; }
+-
+-private:
+- const int signal;
+- bool pending; // Whether the signal is already pending.
+- bool unblock; // Whether to unblock the signal on destruction.
+-};
+-
+-} // namespace internal {
+-
+-#define suppress(signal) \
+- if (os::signals::internal::Suppressor suppressor ## signal = \
+- os::signals::internal::Suppressor(signal))
+-
+-} // namespace signals {
+-
+-} // namespace os {
+-
+-#endif // __STOUT_OS_SIGNALS_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/os/sysctl.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/os/sysctl.hpp
+deleted file mode 100644
+index 065aada..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/os/sysctl.hpp
++++ /dev/null
+@@ -1,274 +0,0 @@
+-#ifndef __STOUT_OS_SYSCTL_HPP__
+-#define __STOUT_OS_SYSCTL_HPP__
+-
+-// Only provide sysctl support for OS X.
+-#ifndef __APPLE__
+-#error "stout/os/sysctl.hpp is only available on OS X."
+-#endif
+-
+-#include <string>
+-#include <vector>
+-
+-#include <sys/types.h>
+-#include <sys/sysctl.h>
+-
+-#include <stout/error.hpp>
+-#include <stout/none.hpp>
+-#include <stout/option.hpp>
+-#include <stout/strings.hpp>
+-#include <stout/try.hpp>
+-
+-namespace os {
+-
+-// Provides an abstraction for getting system information via the
+-// underlying 'sysctl' system call. You describe the sysctl
+-// "Management Information Base" (MIB) name via the constructor, for
+-// example, to describe "maximum number of processes allowed in the
+-// system" you would do:
+-//
+-// os::sysctl(CTL_KERN, KERN_MAXPROC)
+-//
+-// To _retrieve_ the value you need to use one of the 'integer',
+-// 'string', or 'table' methods to indicate the type of the value
+-// being retrieved. For example:
+-//
+-// Try<int> maxproc = os::sysctl(CTL_KERN, KERN_MAXPROC).integer();
+-//
+-// Note that the 'table' method requires specifying a length. If you
+-// would like the length to be looked up dynamically you can just pass
+-// None. Here's an example using 'table' that builds on above:
+-//
+-// Try<vector<kinfo_proc> > processes =
+-// os::sysctl(CTL_KERN, KERN_PROC, KERN_PROC_ALL).table(maxprox.get());
+-//
+-// TODO(benh): Provide an 'integer(i)', 'string(s)', and 'table(t)' to
+-// enable setting system information.
+-struct sysctl
+-{
+- // Note that we create a constructor for each number of levels
+- // because we can't pick a suitable default for unused levels (in
+- // order to distinguish no value from some value) and while Option
+- // would solve that it could also cause people to use None which
+- // we'd need to later handle as an error.
+- explicit sysctl(int level1);
+- sysctl(int level1, int level2);
+- sysctl(int level1, int level2, int level3);
+- sysctl(int level1, int level2, int level3, int level4);
+- sysctl(int level1, int level2, int level3, int level4, int level5);
+- ~sysctl();
+-
+- // Get system information as an integer.
+-private: struct Integer; // Forward declaration.
+-public:
+- Integer integer() const;
+-
+- // Get system information as a string.
+- Try<std::string> string() const;
+-
+- // Get system information as a table, optionally specifying a
+- // length. Note that this function is lazy and will not actually
+- // perform the syscall until you cast (implicitely or explicitly) a
+- // 'Table' to a std::vector<T>. For example, to get the first 10
+- // processes in the process table you can do:
+- //
+- // Try<std::vector<kinfo_proc> > processes =
+- // os::sysctl(CTL_KERN, KERN_PROC, KERN_PROC_ALL).table(10);
+- //
+-private: struct Table; // Forward declaration.
+-public:
+- Table table(const Option<size_t>& length = None()) const;
+-
+-private:
+- struct Integer
+- {
+- Integer(int _levels, int* _name);
+-
+- template <typename T>
+- operator Try<T> ();
+-
+- const int levels;
+- int* name;
+- };
+-
+- struct Table
+- {
+- Table(int _levels, int* _name, const Option<size_t>& _length);
+-
+- template <typename T>
+- operator Try<std::vector<T> > ();
+-
+- const int levels;
+- int* name;
+- Option<size_t> length;
+- };
+-
+- const int levels;
+- int* name;
+-};
+-
+-
+-inline sysctl::sysctl(int level1)
+- : levels(1), name(new int[levels])
+-{
+- name[0] = level1;
+-}
+-
+-
+-inline sysctl::sysctl(int level1, int level2)
+- : levels(2), name(new int[levels])
+-{
+- name[0] = level1;
+- name[1] = level2;
+-}
+-
+-
+-inline sysctl::sysctl(int level1, int level2, int level3)
+- : levels(3), name(new int[levels])
+-{
+- name[0] = level1;
+- name[1] = level2;
+- name[2] = level3;
+-}
+-
+-
+-inline sysctl::sysctl(int level1, int level2, int level3, int level4)
+- : levels(4), name(new int[levels])
+-{
+- name[0] = level1;
+- name[1] = level2;
+- name[2] = level3;
+- name[3] = level4;
+-}
+-
+-
+-inline sysctl::sysctl(int level1, int level2, int level3, int level4, int level5)
+- : levels(5), name(new int[levels])
+-{
+- name[0] = level1;
+- name[1] = level2;
+- name[2] = level3;
+- name[3] = level4;
+- name[4] = level5;
+-}
+-
+-
+-inline sysctl::~sysctl()
+-{
+- delete[] name;
+-}
+-
+-
+-inline sysctl::Integer sysctl::integer() const
+-{
+- return Integer(levels, name);
+-}
+-
+-
+-inline Try<std::string> sysctl::string() const
+-{
+- // First determine the size of the string.
+- size_t size = 0;
+- if (::sysctl(name, levels, NULL, &size, NULL, 0) == -1) {
+- return ErrnoError();
+- }
+-
+- // Now read it.
+- size_t length = size / sizeof(char);
+- char* temp = new char[length];
+- if (::sysctl(name, levels, temp, &size, NULL, 0) == -1) {
+- Error error = ErrnoError();
+- delete[] temp;
+- return error;
+- }
+-
+- // TODO(benh): It's possible that the value has changed since we
+- // determined it's length above. We should really check that we
+- // get back the same length and if not throw an error.
+-
+- // The "string" in 'temp' might include null bytes, so to get all of
+- // the data we need to create a string with 'size' (but we exclude
+- // the last null byte via 'size - 1').
+- std::string result(temp, size - 1);
+- delete[] temp;
+- return result;
+-}
+-
+-
+-inline sysctl::Table sysctl::table(const Option<size_t>& length) const
+-{
+- return Table(levels, name, length);
+-}
+-
+-
+-inline sysctl::Integer::Integer(
+- int _levels,
+- int* _name)
+- : levels(_levels),
+- name(_name)
+-{}
+-
+-
+-template <typename T>
+-sysctl::Integer::operator Try<T> ()
+-{
+- T i;
+- size_t size = sizeof(i);
+- if (::sysctl(name, levels, &i, &size, NULL, 0) == -1) {
+- return ErrnoError();
+- }
+- return i;
+-}
+-
+-
+-inline sysctl::Table::Table(
+- int _levels,
+- int* _name,
+- const Option<size_t>& _length)
+- : levels(_levels),
+- name(_name),
+- length(_length)
+-{}
+-
+-
+-template <typename T>
+-sysctl::Table::operator Try<std::vector<T> > ()
+-{
+- size_t size = 0;
+- if (length.isNone()) {
+- if (::sysctl(name, levels, NULL, &size, NULL, 0) == -1) {
+- return ErrnoError();
+- }
+- if (size % sizeof(T) != 0) {
+- return Error("Failed to determine the length of result, "
+- "amount of available data is not a multiple "
+- "of the table type");
+- }
+- length = Option<size_t>(size / sizeof(T));
+- }
+-
+- T* ts = new T[length.get()];
+- size = length.get() * sizeof(T);
+- if (::sysctl(name, levels, ts, &size, NULL, 0) == -1) {
+- Error error = ErrnoError();
+- delete[] ts;
+- return error;
+- }
+-
+- // TODO(benh): It's possible that the value has changed since we
+- // determined it's length above (or from what was specified). We
+- // should really check that we get back the same length and if not
+- // throw an error.
+-
+- length = size / sizeof(T);
+-
+- std::vector<T> results;
+- for (size_t i = 0; i < length.get(); i++) {
+- results.push_back(ts[i]);
+- }
+- delete[] ts;
+- return results;
+-}
+-
+-} // namespace os {
+-
+-#endif // __STOUT_OS_SYSCTL_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/path.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/path.hpp
+deleted file mode 100644
+index fda4e04..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/path.hpp
++++ /dev/null
+@@ -1,76 +0,0 @@
+-#ifndef __STOUT_PATH_HPP__
+-#define __STOUT_PATH_HPP__
+-
+-#include <string>
+-#include <vector>
+-
+-#include "strings.hpp"
+-
+-namespace path {
+-
+-inline std::string join(const std::string& path1, const std::string& path2)
+-{
+- return
+- strings::remove(path1, "/", strings::SUFFIX) + "/" +
+- strings::remove(path2, "/", strings::PREFIX);
+-}
+-
+-
+-inline std::string join(
+- const std::string& path1,
+- const std::string& path2,
+- const std::string& path3)
+-{
+- return join(path1, join(path2, path3));
+-}
+-
+-
+-inline std::string join(
+- const std::string& path1,
+- const std::string& path2,
+- const std::string& path3,
+- const std::string& path4)
+-{
+- return join(path1, join(path2, path3, path4));
+-}
+-
+-
+-inline std::string join(
+- const std::string& path1,
+- const std::string& path2,
+- const std::string& path3,
+- const std::string& path4,
+- const std::string& path5)
+-{
+- return join(path1, join(path2, join(path3, join(path4, path5))));
+-}
+-
+-
+-inline std::string join(
+- const std::string& path1,
+- const std::string& path2,
+- const std::string& path3,
+- const std::string& path4,
+- const std::string& path5,
+- const std::string& path6)
+-{
+- return join(path1, join(path2, path3, path4, path5, path6));
+-}
+-
+-
+-inline std::string join(const std::vector<std::string>& paths)
+-{
+- if (paths.empty()) {
+- return "";
+- }
+-
+- std::string result = paths[0];
+- for (size_t i = 1; i < paths.size(); ++i) {
+- result = join(result, paths[i]);
+- }
+- return result;
+-}
+-
+-} // namespace path {
+-
+-#endif // __STOUT_PATH_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/preprocessor.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/preprocessor.hpp
+deleted file mode 100644
+index bd9c411..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/preprocessor.hpp
++++ /dev/null
+@@ -1,29 +0,0 @@
+-#ifndef __STOUT_PROCESS_PREPROCESSOR_HPP__
+-#define __STOUT_PROCESS_PREPROCESSOR_HPP__
+-
+-#include <boost/preprocessor/cat.hpp>
+-
+-#include <boost/preprocessor/arithmetic/inc.hpp>
+-
+-#include <boost/preprocessor/facilities/intercept.hpp>
+-
+-#include <boost/preprocessor/repetition/enum_params.hpp>
+-#include <boost/preprocessor/repetition/enum_binary_params.hpp>
+-#include <boost/preprocessor/repetition/enum_trailing_params.hpp>
+-#include <boost/preprocessor/repetition/repeat.hpp>
+-#include <boost/preprocessor/repetition/repeat_from_to.hpp>
+-
+-// Provides aliases to a bunch of preprocessor macros useful for
+-// creating template definitions that have varying number of
+-// parameters (should be removable with C++-11 variadic templates).
+-
+-#define CAT BOOST_PP_CAT
+-#define INC BOOST_PP_INC
+-#define INTERCEPT BOOST_PP_INTERCEPT
+-#define ENUM_PARAMS BOOST_PP_ENUM_PARAMS
+-#define ENUM_BINARY_PARAMS BOOST_PP_ENUM_BINARY_PARAMS
+-#define ENUM_TRAILING_PARAMS BOOST_PP_ENUM_TRAILING_PARAMS
+-#define REPEAT BOOST_PP_REPEAT
+-#define REPEAT_FROM_TO BOOST_PP_REPEAT_FROM_TO
+-
+-#endif // __STOUT_PROCESS_PREPROCESSOR_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/proc.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/proc.hpp
+deleted file mode 100644
+index 1bbbaa1..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/proc.hpp
++++ /dev/null
+@@ -1,493 +0,0 @@
+-/**
+- * Licensed to the Apache Software Foundation (ASF) under one
+- * or more contributor license agreements. See the NOTICE file
+- * distributed with this work for additional information
+- * regarding copyright ownership. The ASF licenses this file
+- * to you under the Apache License, Version 2.0 (the
+- * "License"); you may not use this file except in compliance
+- * with the License. You may obtain a copy of the License at
+- *
+- * http://www.apache.org/licenses/LICENSE-2.0
+- *
+- * Unless required by applicable law or agreed to in writing, software
+- * distributed under the License is distributed on an "AS IS" BASIS,
+- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+- * See the License for the specific language governing permissions and
+- * limitations under the License.
+- */
+-
+-#ifndef __STOUT_PROC_HPP__
+-#define __STOUT_PROC_HPP__
+-
+-// This file contains linux-only utilities for /proc.
+-#ifndef __linux__
+-#error "stout/proc.hpp is only available on Linux systems."
+-#endif
+-
+-#include <errno.h>
+-#include <signal.h>
+-
+-#include <sys/types.h> // For pid_t.
+-
+-#include <fstream>
+-#include <list>
+-#include <queue>
+-#include <set>
+-#include <sstream> // For 'std::istringstream'.
+-#include <string>
+-#include <vector>
+-
+-#include <stout/error.hpp>
+-#include <stout/foreach.hpp>
+-#include <stout/none.hpp>
+-#include <stout/numify.hpp>
+-#include <stout/option.hpp>
+-#include <stout/strings.hpp>
+-#include <stout/try.hpp>
+-
+-#include <stout/os/exists.hpp>
+-#include <stout/os/ls.hpp>
+-#include <stout/os/read.hpp>
+-
+-namespace proc {
+-
+-// Snapshot of a process (modeled after /proc/[pid]/stat).
+-// For more information, see:
+-// http://www.kernel.org/doc/Documentation/filesystems/proc.txt
+-struct ProcessStatus
+-{
+- ProcessStatus(
+- pid_t _pid,
+- const std::string& _comm,
+- char _state,
+- pid_t _ppid,
+- pid_t _pgrp,
+- pid_t _session,
+- int _tty_nr,
+- pid_t _tpgid,
+- unsigned int _flags,
+- unsigned long _minflt,
+- unsigned long _cminflt,
+- unsigned long _majflt,
+- unsigned long _cmajflt,
+- unsigned long _utime,
+- unsigned long _stime,
+- long _cutime,
+- long _cstime,
+- long _priority,
+- long _nice,
+- long _num_threads,
+- long _itrealvalue,
+- unsigned long long _starttime,
+- unsigned long _vsize,
+- long _rss,
+- unsigned long _rsslim,
+- unsigned long _startcode,
+- unsigned long _endcode,
+- unsigned long _startstack,
+- unsigned long _kstkeip,
+- unsigned long _signal,
+- unsigned long _blocked,
+- unsigned long _sigcatch,
+- unsigned long _wchan,
+- unsigned long _nswap,
+- unsigned long _cnswap)
+- : pid(_pid),
+- comm(_comm),
+- state(_state),
+- ppid(_ppid),
+- pgrp(_pgrp),
+- session(_session),
+- tty_nr(_tty_nr),
+- tpgid(_tpgid),
+- flags(_flags),
+- minflt(_minflt),
+- cminflt(_cminflt),
+- majflt(_majflt),
+- cmajflt(_cmajflt),
+- utime(_utime),
+- stime(_stime),
+- cutime(_cutime),
+- cstime(_cstime),
+- priority(_priority),
+- nice(_nice),
+- num_threads(_num_threads),
+- itrealvalue(_itrealvalue),
+- starttime(_starttime),
+- vsize(_vsize),
+- rss(_rss),
+- rsslim(_rsslim),
+- startcode(_startcode),
+- endcode(_endcode),
+- startstack(_startstack),
+- kstkeip(_kstkeip),
+- signal(_signal),
+- blocked(_blocked),
+- sigcatch(_sigcatch),
+- wchan(_wchan),
+- nswap(_nswap),
+- cnswap(_cnswap) {}
+-
+- const pid_t pid;
+- const std::string comm;
+- const char state;
+- const pid_t ppid;
+- const pid_t pgrp;
+- const pid_t session;
+- const int tty_nr;
+- const pid_t tpgid;
+- const unsigned int flags;
+- const unsigned long minflt;
+- const unsigned long cminflt;
+- const unsigned long majflt;
+- const unsigned long cmajflt;
+- const unsigned long utime;
+- const unsigned long stime;
+- const long cutime;
+- const long cstime;
+- const long priority;
+- const long nice;
+- const long num_threads;
+- const long itrealvalue;
+- const unsigned long long starttime;
+- const unsigned long vsize;
+- const long rss;
+- const unsigned long rsslim;
+- const unsigned long startcode;
+- const unsigned long endcode;
+- const unsigned long startstack;
+- const unsigned long kstkeip;
+- const unsigned long signal;
+- const unsigned long blocked;
+- const unsigned long sigcatch;
+- const unsigned long wchan;
+- const unsigned long nswap;
+- const unsigned long cnswap;
+-};
+-
+-
+-// Returns the process statistics from /proc/[pid]/stat.
+-// The return value is None if the process does not exist.
+-inline Result<ProcessStatus> status(pid_t pid)
+-{
+- std::string path = "/proc/" + stringify(pid) + "/stat";
+-
+- Try<std::string> read = os::read(path);
+- if (read.isError()) {
+- // Need to check if file exists AFTER we open it to guarantee
+- // process hasn't terminated.
+- if (!os::exists(path)) {
+- return None();
+- }
+- return Error(read.error());
+- }
+-
+- std::istringstream data(read.get());
+-
+- std::string comm;
+- char state;
+- pid_t ppid;
+- pid_t pgrp;
+- pid_t session;
+- int tty_nr;
+- pid_t tpgid;
+- unsigned int flags;
+- unsigned long minflt;
+- unsigned long cminflt;
+- unsigned long majflt;
+- unsigned long cmajflt;
+- unsigned long utime;
+- unsigned long stime;
+- long cutime;
+- long cstime;
+- long priority;
+- long nice;
+- long num_threads;
+- long itrealvalue;
+- unsigned long long starttime;
+- unsigned long vsize;
+- long rss;
+- unsigned long rsslim;
+- unsigned long startcode;
+- unsigned long endcode;
+- unsigned long startstack;
+- unsigned long kstkeip;
+- unsigned long signal;
+- unsigned long blocked;
+- unsigned long sigcatch;
+- unsigned long wchan;
+- unsigned long nswap;
+- unsigned long cnswap;
+-
+- // NOTE: The following are unused for now.
+- // int exit_signal;
+- // int processor;
+- // unsigned int rt_priority;
+- // unsigned int policy;
+- // unsigned long long delayacct_blkio_ticks;
+- // unsigned long guest_time;
+- // unsigned int cguest_time;
+-
+- std::string _; // For ignoring fields.
+-
+- // Parse all fields from stat.
+- data >> _ >> comm >> state >> ppid >> pgrp >> session >> tty_nr
+- >> tpgid >> flags >> minflt >> cminflt >> majflt >> cmajflt
+- >> utime >> stime >> cutime >> cstime >> priority >> nice
+- >> num_threads >> itrealvalue >> starttime >> vsize >> rss
+- >> rsslim >> startcode >> endcode >> startstack >> kstkeip
+- >> signal >> blocked >> sigcatch >> wchan >> nswap >> cnswap;
+-
+- // Check for any read/parse errors.
+- if (data.fail() && !data.eof()) {
+- return Error("Failed to read/parse '" + path + "'");
+- }
+-
+- // Remove the parentheses that is wrapped around 'comm' (when
+- // printing out the process in a process tree we use parentheses to
+- // indicate "zombie" processes).
+- comm = strings::remove(comm, "(", strings::PREFIX);
+- comm = strings::remove(comm, ")", strings::SUFFIX);
+-
+- return ProcessStatus(pid, comm, state, ppid, pgrp, session, tty_nr,
+- tpgid, flags, minflt, cminflt, majflt, cmajflt,
+- utime, stime, cutime, cstime, priority, nice,
+- num_threads, itrealvalue, starttime, vsize, rss,
+- rsslim, startcode, endcode, startstack, kstkeip,
+- signal, blocked, sigcatch, wchan, nswap, cnswap);
+-}
+-
+-
+-inline Result<std::string> cmdline(const Option<pid_t>& pid = None())
+-{
+- const std::string path = pid.isSome()
+- ? "/proc/" + stringify(pid.get()) + "/cmdline"
+- : "/proc/cmdline";
+-
+- std::ifstream file(path.c_str());
+-
+- if (!file.is_open()) {
+- // Need to check if file exists AFTER we open it to guarantee
+- // process hasn't terminated (or if it has, we at least have a
+- // file which the kernel _should_ respect until a close).
+- if (!os::exists(path)) {
+- return None();
+- }
+- return Error("Failed to open '" + path + "'");
+- }
+-
+- std::stringbuf buffer;
+-
+- do {
+- // Read each argument in "argv", separated by null bytes.
+- file.get(buffer, '\0');
+-
+- // Check for any read errors.
+- if (file.fail() && !file.eof()) {
+- file.close();
+- return Error("Failed to read '" + path + "'");
+- } else if (!file.eof()) {
+- file.get(); // Read the null byte.
+- buffer.sputc(' '); // Put a space between each command line argument.
+- }
+- } while (!file.eof());
+-
+- return buffer.str();
+-}
+-
+-
+-// Reads from /proc and returns a list of all running processes.
+-inline Try<std::set<pid_t> > pids()
+-{
+- std::set<pid_t> pids;
+-
+- foreach (const std::string& file, os::ls("/proc")) {
+- Try<pid_t> pid = numify<pid_t>(file);
+- if (pid.isSome()) {
+- pids.insert(pid.get()); // Ignore files that can't be numified.
+- }
+- }
+-
+- if (!pids.empty()) {
+- return pids;
+- }
+-
+- return Error("Failed to determine pids from /proc");
+-}
+-
+-
+-// Snapshot of a system (modeled after /proc/stat).
+-struct SystemStatus
+-{
+- SystemStatus(unsigned long long _btime) : btime(_btime) {}
+-
+- const unsigned long long btime; // Boot time.
+- // TODO(benh): Add more.
+-};
+-
+-
+-// Returns the system statistics from /proc/stat.
+-inline Try<SystemStatus> status()
+-{
+- unsigned long long btime = 0;
+-
+- std::ifstream file("/proc/stat");
+-
+- if (!file.is_open()) {
+- return Error("Failed to open /proc/stat");
+- }
+-
+- std::string line;
+- while (std::getline(file, line)) {
+- if (line.find("btime ") == 0) {
+- Try<unsigned long long> number =
+- numify<unsigned long long>(line.substr(6));
+-
+- if (number.isError()) {
+- return Error("Failed to parse /proc/stat: " + number.error());
+- }
+-
+- btime = number.get();
+- break;
+- }
+- }
+-
+- if (file.fail() && !file.eof()) {
+- file.close();
+- return Error("Failed to read /proc/stat");
+- }
+-
+- file.close();
+-
+- return SystemStatus(btime);
+-}
+-
+-
+-// Representation of a processor (really an execution unit since this
+-// captures "hardware threads" as well) modeled after /proc/cpuinfo.
+-struct CPU
+-{
+- CPU(unsigned int _id, unsigned int _core, unsigned int _socket)
+- : id(_id), core(_core), socket(_socket) {}
+-
+- // These are non-const because we need the default assignment operator.
+- unsigned int id; // "processor"
+- unsigned int core; // "core id"
+- unsigned int socket; // "physical id"
+-};
+-
+-
+-inline bool operator == (const CPU& lhs, const CPU& rhs)
+-{
+- return (lhs.id == rhs.id) && (lhs.core == rhs.core) &&
+- (lhs.socket == rhs.socket);
+-}
+-
+-
+-inline bool operator < (const CPU& lhs, const CPU& rhs)
+-{
+- // Sort by (socket, core, id).
+- if (lhs.socket != rhs.socket) {
+- return lhs.socket < rhs.socket;
+- }
+-
+- // On the same socket.
+- if (lhs.core != rhs.core) {
+- return lhs.core < rhs.core;
+- }
+-
+- // On the same core.
+- return lhs.id < rhs.id;
+-}
+-
+-
+-inline std::ostream& operator << (std::ostream& out, const CPU& cpu)
+-{
+- return out << "CPU (id:" << cpu.id << ", "
+- << "core:" << cpu.core << ", "
+- << "socket:" << cpu.socket << ")";
+-}
+-
+-
+-// Reads from /proc/cpuinfo and returns a list of CPUs.
+-inline Try<std::list<CPU> > cpus()
+-{
+- std::list<CPU> results;
+-
+- std::ifstream file("/proc/cpuinfo");
+-
+- if (!file.is_open()) {
+- return Error("Failed to open /proc/cpuinfo");
+- }
+-
+- // Placeholders as we parse the file.
+- Option<unsigned int> id;
+- Option<unsigned int> core;
+- Option<unsigned int> socket;
+-
+- std::string line;
+- while (std::getline(file, line)) {
+- if (line.find("processor") == 0 ||
+- line.find("physical id") == 0 ||
+- line.find("core id") == 0) {
+- // Get out and parse the value.
+- std::vector<std::string> tokens = strings::tokenize(line, ": ");
+-
+- if (tokens.size() < 2) {
+- return Error("Unexpected format in /proc/cpuinfo: " +
+- stringify(tokens));
+- }
+-
+- Try<unsigned int> value = numify<unsigned int>(tokens.back());
+- if (value.isError()) {
+- return Error(value.error());
+- }
+-
+- // Now save the value.
+- if (line.find("processor") == 0) {
+- if (id.isSome()) {
+- // The physical id and core id are not present in this case.
+- results.push_back(CPU(id.get(), 0, 0));
+- }
+- id = value.get();
+- } else if (line.find("physical id") == 0) {
+- if (socket.isSome()) {
+- return Error("Unexpected format in /proc/cpuinfo");
+- }
+- socket = value.get();
+- } else if (line.find("core id") == 0) {
+- if (core.isSome()) {
+- return Error("Unexpected format in /proc/cpuinfo");
+- }
+- core = value.get();
+- }
+-
+- // And finally create a CPU if we have all the information.
+- if (id.isSome() && core.isSome() && socket.isSome()) {
+- results.push_back(CPU(id.get(), core.get(), socket.get()));
+- id = None();
+- core = None();
+- socket = None();
+- }
+- }
+- }
+-
+- // Add the last processor if the physical id and core id were not present.
+- if (id.isSome()) {
+- // The physical id and core id are not present.
+- results.push_back(CPU(id.get(), 0, 0));
+- }
+-
+- if (file.fail() && !file.eof()) {
+- file.close();
+- return Error("Failed to read /proc/cpuinfo");
+- }
+-
+- file.close();
+-
+- return results;
+-}
+-
+-} // namespace proc {
+-
+-#endif // __STOUT_PROC_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/protobuf.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/protobuf.hpp
+deleted file mode 100644
+index 3fa7fe6..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/protobuf.hpp
++++ /dev/null
+@@ -1,312 +0,0 @@
+-#ifndef __STOUT_PROTOBUF_HPP__
+-#define __STOUT_PROTOBUF_HPP__
+-
+-#include <assert.h>
+-#include <errno.h>
+-#include <stdint.h>
+-#include <unistd.h>
+-
+-#include <sys/types.h>
+-
+-#include <glog/logging.h>
+-
+-#include <google/protobuf/descriptor.h>
+-#include <google/protobuf/message.h>
+-
+-#include <google/protobuf/io/zero_copy_stream_impl.h>
+-
+-#include <string>
+-
+-#include <boost/lexical_cast.hpp>
+-
+-#include "error.hpp"
+-#include "json.hpp"
+-#include "none.hpp"
+-#include "os.hpp"
+-#include "result.hpp"
+-#include "try.hpp"
+-
+-namespace protobuf {
+-
+-// Write out the given protobuf to the specified file descriptor by
+-// first writing out the length of the protobuf followed by the contents.
+-// NOTE: On error, this may have written partial data to the file.
+-inline Try<Nothing> write(int fd, const google::protobuf::Message& message)
+-{
+- if (!message.IsInitialized()) {
+- return Error("Uninitialized protocol buffer");
+- }
+-
+- // First write the size of the protobuf.
+- uint32_t size = message.ByteSize();
+- std::string bytes = std::string((char*) &size, sizeof(size));
+-
+- Try<Nothing> result = os::write(fd, bytes);
+- if (result.isError()) {
+- return Error("Failed to write size: " + result.error());
+- }
+-
+- if (!message.SerializeToFileDescriptor(fd)) {
+- return Error("Failed to write/serialize message");
+- }
+-
+- return Nothing();
+-}
+-
+-
+-// A wrapper function that wraps the above write with open and closing the file.
+-inline Try<Nothing> write(
+- const std::string& path,
+- const google::protobuf::Message& message)
+-{
+- Try<int> fd = os::open(
+- path,
+- O_WRONLY | O_CREAT | O_TRUNC,
+- S_IRUSR | S_IWUSR | S_IRGRP | S_IRWXO);
+-
+- if (fd.isError()) {
+- return Error("Failed to open file '" + path + "': " + fd.error());
+- }
+-
+- Try<Nothing> result = write(fd.get(), message);
+-
+- // NOTE: We ignore the return value of close(). This is because users calling
+- // this function are interested in the return value of write(). Also an
+- // unsuccessful close() doesn't affect the write.
+- os::close(fd.get());
+-
+- return result;
+-}
+-
+-
+-// Read the next protobuf of type T from the file by first reading
+-// the "size" followed by the contents (as written by 'write' above).
+-// If 'ignorePartial' is true, None() is returned when we unexpectedly
+-// hit EOF while reading the protobuf (e.g., partial write).
+-template <typename T>
+-inline Result<T> read(int fd, bool ignorePartial = false)
+-{
+- // Save the offset so we can re-adjust if something goes wrong.
+- off_t offset = lseek(fd, 0, SEEK_CUR);
+- if (offset == -1) {
+- return ErrnoError("Failed to lseek to SEEK_CUR");
+- }
+-
+- uint32_t size;
+- Result<std::string> result = os::read(fd, sizeof(size));
+-
+- if (result.isNone()) {
+- return None(); // No more protobufs to read.
+- } else if (result.isError()) {
+- return Error("Failed to read size: " + result.error());
+- }
+-
+- // Parse the size from the bytes.
+- memcpy((void*) &size, (void*) result.get().data(), sizeof(size));
+-
+- // NOTE: Instead of specifically checking for corruption in 'size', we simply
+- // try to read 'size' bytes. If we hit EOF early, it is an indication of
+- // corruption.
+- result = os::read(fd, size);
+-
+- if (result.isNone()) {
+- // Hit EOF unexpectedly. Restore the offset to before the size read.
+- lseek(fd, offset, SEEK_SET);
+- if (ignorePartial) {
+- return None();
+- }
+- return Error("Failed to read message of size " + stringify(size) +
+- " bytes: hit EOF unexpectedly, possible corruption");
+- } else if (result.isError()) {
+- // Restore the offset to before the size read.
+- lseek(fd, offset, SEEK_SET);
+- return Error("Failed to read message: " + result.error());
+- }
+-
+- // Parse the protobuf from the string.
+- T message;
+- google::protobuf::io::ArrayInputStream stream(
+- result.get().data(), result.get().size());
+-
+- if (!message.ParseFromZeroCopyStream(&stream)) {
+- // Restore the offset to before the size read.
+- lseek(fd, offset, SEEK_SET);
+- return Error("Failed to deserialize message");
+- }
+-
+- return message;
+-}
+-
+-
+-// A wrapper function that wraps the above read() with
+-// open and closing the file.
+-template <typename T>
+-inline Result<T> read(const std::string& path)
+-{
+- Try<int> fd = os::open(
+- path, O_RDONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IRWXO);
+-
+- if (fd.isError()) {
+- return Error("Failed to open file '" + path + "': " + fd.error());
+- }
+-
+- Result<T> result = read<T>(fd.get());
+-
+- // NOTE: We ignore the return value of close(). This is because users calling
+- // this function are interested in the return value of read(). Also an
+- // unsuccessful close() doesn't affect the read.
+- os::close(fd.get());
+-
+- return result;
+-}
+-
+-} // namespace protobuf {
+-
+-namespace JSON {
+-
+-struct Protobuf
+-{
+- // TODO(bmahler): This currently uses the default value for optional
+- // fields but we may want to revisit this decision.
+- Protobuf(const google::protobuf::Message& message)
+- {
+- const google::protobuf::Reflection* reflection = message.GetReflection();
+- std::vector<const google::protobuf::FieldDescriptor*> fields;
+- reflection->ListFields(message, &fields);
+-
+- foreach (const google::protobuf::FieldDescriptor* field, fields) {
+- if (field->is_repeated()) {
+- JSON::Array array;
+- for (int i = 0; i < reflection->FieldSize(message, field); ++i) {
+- switch (field->type()) {
+- case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
+- array.values.push_back(JSON::Number(
+- reflection->GetRepeatedDouble(message, field, i)));
+- break;
+- case google::protobuf::FieldDescriptor::TYPE_FLOAT:
+- array.values.push_back(JSON::Number(
+- reflection->GetRepeatedFloat(message, field, i)));
+- break;
+- case google::protobuf::FieldDescriptor::TYPE_INT64:
+- case google::protobuf::FieldDescriptor::TYPE_SINT64:
+- case google::protobuf::FieldDescriptor::TYPE_SFIXED64:
+- array.values.push_back(JSON::Number(
+- reflection->GetRepeatedInt64(message, field, i)));
+- break;
+- case google::protobuf::FieldDescriptor::TYPE_UINT64:
+- case google::protobuf::FieldDescriptor::TYPE_FIXED64:
+- array.values.push_back(JSON::Number(
+- reflection->GetRepeatedUInt64(message, field, i)));
+- break;
+- case google::protobuf::FieldDescriptor::TYPE_INT32:
+- case google::protobuf::FieldDescriptor::TYPE_SINT32:
+- case google::protobuf::FieldDescriptor::TYPE_SFIXED32:
+- array.values.push_back(JSON::Number(
+- reflection->GetRepeatedInt32(message, field, i)));
+- break;
+- case google::protobuf::FieldDescriptor::TYPE_UINT32:
+- case google::protobuf::FieldDescriptor::TYPE_FIXED32:
+- array.values.push_back(JSON::Number(
+- reflection->GetRepeatedUInt32(message, field, i)));
+- break;
+- case google::protobuf::FieldDescriptor::TYPE_BOOL:
+- if (reflection->GetRepeatedBool(message, field, i)) {
+- array.values.push_back(JSON::True());
+- } else {
+- array.values.push_back(JSON::False());
+- }
+- break;
+- case google::protobuf::FieldDescriptor::TYPE_STRING:
+- case google::protobuf::FieldDescriptor::TYPE_BYTES:
+- array.values.push_back(JSON::String(
+- reflection->GetRepeatedString(message, field, i)));
+- break;
+- case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
+- array.values.push_back(Protobuf(
+- reflection->GetRepeatedMessage(message, field, i)));
+- break;
+- case google::protobuf::FieldDescriptor::TYPE_ENUM:
+- array.values.push_back(JSON::String(
+- reflection->GetRepeatedEnum(message, field, i)->name()));
+- break;
+- case google::protobuf::FieldDescriptor::TYPE_GROUP:
+- // Deprecated!
+- default:
+- std::cerr << "Unhandled protobuf field type: " << field->type()
+- << std::endl;
+- abort();
+- }
+- }
+- object.values[field->name()] = array;
+- } else {
+- switch (field->type()) {
+- case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
+- object.values[field->name()] =
+- JSON::Number(reflection->GetDouble(message, field));
+- break;
+- case google::protobuf::FieldDescriptor::TYPE_FLOAT:
+- object.values[field->name()] =
+- JSON::Number(reflection->GetFloat(message, field));
+- break;
+- case google::protobuf::FieldDescriptor::TYPE_INT64:
+- case google::protobuf::FieldDescriptor::TYPE_SINT64:
+- case google::protobuf::FieldDescriptor::TYPE_SFIXED64:
+- object.values[field->name()] =
+- JSON::Number(reflection->GetInt64(message, field));
+- break;
+- case google::protobuf::FieldDescriptor::TYPE_UINT64:
+- case google::protobuf::FieldDescriptor::TYPE_FIXED64:
+- object.values[field->name()] =
+- JSON::Number(reflection->GetUInt64(message, field));
+- break;
+- case google::protobuf::FieldDescriptor::TYPE_INT32:
+- case google::protobuf::FieldDescriptor::TYPE_SINT32:
+- case google::protobuf::FieldDescriptor::TYPE_SFIXED32:
+- object.values[field->name()] =
+- JSON::Number(reflection->GetInt32(message, field));
+- break;
+- case google::protobuf::FieldDescriptor::TYPE_UINT32:
+- case google::protobuf::FieldDescriptor::TYPE_FIXED32:
+- object.values[field->name()] =
+- JSON::Number(reflection->GetUInt32(message, field));
+- break;
+- case google::protobuf::FieldDescriptor::TYPE_BOOL:
+- if (reflection->GetBool(message, field)) {
+- object.values[field->name()] = JSON::True();
+- } else {
+- object.values[field->name()] = JSON::False();
+- }
+- break;
+- case google::protobuf::FieldDescriptor::TYPE_STRING:
+- case google::protobuf::FieldDescriptor::TYPE_BYTES:
+- object.values[field->name()] =
+- JSON::String(reflection->GetString(message, field));
+- break;
+- case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
+- object.values[field->name()] =
+- Protobuf(reflection->GetMessage(message, field));
+- break;
+- case google::protobuf::FieldDescriptor::TYPE_ENUM:
+- object.values[field->name()] =
+- JSON::String(reflection->GetEnum(message, field)->name());
+- break;
+- case google::protobuf::FieldDescriptor::TYPE_GROUP:
+- // Deprecated!
+- default:
+- std::cerr << "Unhandled protobuf field type: " << field->type()
+- << std::endl;
+- abort();
+- }
+- }
+- }
+- }
+-
+- operator Object () const { return object; }
+-
+-private:
+- JSON::Object object;
+-};
+-
+-} // namespace JSON {
+-
+-#endif // __STOUT_PROTOBUF_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/result.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/result.hpp
+deleted file mode 100644
+index f918f86..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/result.hpp
++++ /dev/null
+@@ -1,99 +0,0 @@
+-#ifndef __STOUT_RESULT_HPP__
+-#define __STOUT_RESULT_HPP__
+-
+-#include <assert.h>
+-#include <stdlib.h> // For abort.
+-
+-#include <iostream>
+-#include <string>
+-
+-
+-template <typename T>
+-class Result
+-{
+-public:
+- static Result<T> none()
+- {
+- return Result<T>(NONE);
+- }
+-
+- static Result<T> some(const T& t)
+- {
+- return Result<T>(SOME, new T(t));
+- }
+-
+- static Result<T> error(const std::string& message)
+- {
+- return Result<T>(ERROR, NULL, message);
+- }
+-
+- Result(const T& _t) : state(SOME), t(new T(_t)) {}
+-
+- Result(const Result<T>& that)
+- {
+- state = that.state;
+- if (that.t != NULL) {
+- t = new T(*that.t);
+- } else {
+- t = NULL;
+- }
+- message = that.message;
+- }
+-
+- ~Result()
+- {
+- delete t;
+- }
+-
+- Result<T>& operator = (const Result<T>& that)
+- {
+- if (this != &that) {
+- delete t;
+- state = that.state;
+- if (that.t != NULL) {
+- t = new T(*that.t);
+- } else {
+- t = NULL;
+- }
+- message = that.message;
+- }
+-
+- return *this;
+- }
+-
+- bool isSome() const { return state == SOME; }
+- bool isNone() const { return state == NONE; }
+- bool isError() const { return state == ERROR; }
+-
+- T get() const
+- {
+- if (state != SOME) {
+- if (state == ERROR) {
+- std::cerr << "Result::get() but state == ERROR: "
+- << error() << std::endl;
+- } else if (state == NONE) {
+- std::cerr << "Result::get() but state == NONE" << std::endl;
+- }
+- abort();
+- }
+- return *t;
+- }
+-
+- std::string error() const { assert(state == ERROR); return message; }
+-
+-private:
+- enum State {
+- SOME,
+- NONE,
+- ERROR
+- };
+-
+- Result(State _state, T* _t = NULL, const std::string& _message = "")
+- : state(_state), t(_t), message(_message) {}
+-
+- State state;
+- T* t;
+- std::string message;
+-};
+-
+-#endif // __STOUT_RESULT_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/set.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/set.hpp
+deleted file mode 100644
+index ba7ffe8..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/set.hpp
++++ /dev/null
+@@ -1,70 +0,0 @@
+-#include <algorithm> // For std::set_intersection.
+-#include <set>
+-#include <vector>
+-
+-template <typename T>
+-class Set : public std::set<T>
+-{
+-public:
+- Set() {}
+-
+- Set(const T& t1)
+- {
+- std::set<T>::insert(t1);
+- }
+-
+- Set(const T& t1, const T& t2)
+- {
+- std::set<T>::insert(t1);
+- std::set<T>::insert(t2);
+- }
+-
+- Set(const T& t1, const T& t2, const T& t3)
+- {
+- std::set<T>::insert(t1);
+- std::set<T>::insert(t2);
+- std::set<T>::insert(t3);
+- }
+-
+- Set(const T& t1, const T& t2, const T& t3, const T& t4)
+- {
+- std::set<T>::insert(t1);
+- std::set<T>::insert(t2);
+- std::set<T>::insert(t3);
+- std::set<T>::insert(t4);
+- }
+-};
+-
+-
+-template <typename T>
+-std::set<T> operator | (const std::set<T>& left, const std::set<T>& right)
+-{
+- // Note, we're not using 'set_union' since it affords us no benefit
+- // in efficiency and is more complicated to use given we have sets.
+- std::set<T> result = left;
+- result.insert(right.begin(), right.end());
+- return result;
+-}
+-
+-
+-template <typename T>
+-std::set<T> operator + (const std::set<T>& left, const T& t)
+-{
+- std::set<T> result = left;
+- result.insert(t);
+- return result;
+-}
+-
+-
+-template <typename T>
+-std::set<T> operator & (const std::set<T>& left, const std::set<T>& right)
+-{
+- std::set<T> result;
+- std::set_intersection(
+- left.begin(),
+- left.end(),
+- right.begin(),
+- right.end(),
+- std::inserter(result, result.begin()));
+- return result;
+-}
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/some.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/some.hpp
+deleted file mode 100644
+index e2f56cc..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/some.hpp
++++ /dev/null
+@@ -1,73 +0,0 @@
+-#ifndef __STOUT_SOME_HPP__
+-#define __STOUT_SOME_HPP__
+-
+-#include <stout/option.hpp>
+-#include <stout/result.hpp>
+-
+-// A "some" type that is implicitely convertable to an Option<T> and
+-// Result<T> for any T (effectively "syntactic sugar" to make code
+-// more readable). The implementation uses cast operators to perform
+-// the conversions instead of adding constructors to Option/Result
+-// directly. The extra copies involved here can be elided with C++11
+-// rvalue references. Furthermore, since in most circumstances a Some
+-// will not be needed (an Option<T> or Result<T> can be constructed
+-// directly from the value) we don't worry about performance.
+-
+-template <typename T>
+-struct _Some
+-{
+- _Some(T _t) : t(_t) {}
+-
+- template <typename U>
+- operator Option<U> () const
+- {
+- return Option<U>::some(t);
+- }
+-
+- // Give the compiler some help for nested Option<U>.
+- template <template <typename> class S, typename U>
+- operator S<Option<U> > () const
+- {
+- return S<Option<U> >(Option<U>::some(t));
+- }
+-
+- template <typename U>
+- operator Result<U> () const
+- {
+- return Result<U>::some(t);
+- }
+-
+- // Give the compiler some help for nested Result<U>.
+- template <template <typename> class S, typename U>
+- operator S<Result<U> > () const
+- {
+- return S<Result<U> >(Result<U>::some(t));
+- }
+-
+- // Give the compiler some more help to disambiguate the above cast
+- // operators from Option<Result<U>>.
+- template <typename U>
+- operator Option<Result<U> > () const
+- {
+- return Option<Result<U> >::some(t);
+- }
+-
+- // Give the compiler some more help to disambiguate the above cast
+- // operators from Result<Option<U>>.
+- template <typename U>
+- operator Result<Option<U> > () const
+- {
+- return Result<Option<U> >::some(t);
+- }
+-
+- const T t;
+-};
+-
+-
+-template <typename T>
+-_Some<T> Some(T t)
+-{
+- return _Some<T>(t);
+-}
+-
+-#endif // __STOUT_SOME_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/stopwatch.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/stopwatch.hpp
+deleted file mode 100644
+index 97e3469..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/stopwatch.hpp
++++ /dev/null
+@@ -1,77 +0,0 @@
+-#ifndef __STOUT_STOPWATCH_HPP__
+-#define __STOUT_STOPWATCH_HPP__
+-
+-#include <time.h>
+-
+-#ifdef __MACH__
+-#include <mach/clock.h>
+-#include <mach/mach.h>
+-#endif // __MACH__
+-
+-#include <sys/time.h>
+-
+-#include "duration.hpp"
+-
+-class Stopwatch
+-{
+-public:
+- Stopwatch()
+- : running(false)
+- {
+- started.tv_sec = 0;
+- started.tv_nsec = 0;
+- stopped.tv_sec = 0;
+- stopped.tv_nsec = 0;
+- }
+-
+- void start()
+- {
+- started = now();
+- running = true;
+- }
+-
+- void stop()
+- {
+- stopped = now();
+- running = false;
+- }
+-
+- Nanoseconds elapsed()
+- {
+- if (!running) {
+- return Nanoseconds(diff(stopped, started));
+- }
+-
+- return Nanoseconds(diff(now(), started));
+- }
+-
+-private:
+- static timespec now()
+- {
+- timespec ts;
+-#ifdef __MACH__
+- // OS X does not have clock_gettime, use clock_get_time.
+- clock_serv_t cclock;
+- mach_timespec_t mts;
+- host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
+- clock_get_time(cclock, &mts);
+- mach_port_deallocate(mach_task_self(), cclock);
+- ts.tv_sec = mts.tv_sec;
+- ts.tv_nsec = mts.tv_nsec;
+-#else
+- clock_gettime(CLOCK_REALTIME, &ts);
+-#endif // __MACH__
+- return ts;
+- }
+-
+- static uint64_t diff(const timespec& from, const timespec& to)
+- {
+- return ((from.tv_sec - to.tv_sec) * 1000000000LL)
+- + (from.tv_nsec - to.tv_nsec);
+- }
+-
+- bool running;
+- timespec started, stopped;
+-};
+-
+-#endif // __STOUT_STOPWATCH_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/stringify.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/stringify.hpp
+deleted file mode 100644
+index 2bb7290..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/stringify.hpp
++++ /dev/null
+@@ -1,141 +0,0 @@
+-#ifndef __STOUT_STRINGIFY_HPP__
+-#define __STOUT_STRINGIFY_HPP__
+-
+-#include <stdlib.h> // For 'abort'.
+-
+-#include <iostream> // For 'std::cerr' and 'std::endl'.
+-#include <list>
+-#include <map>
+-#include <set>
+-#include <sstream> // For 'std::ostringstream'.
+-#include <string>
+-#include <vector>
+-
+-#include "hashmap.hpp"
+-
+-template <typename T>
+-std::string stringify(T t)
+-{
+- std::ostringstream out;
+- out << t;
+- if (!out.good()) {
+- std::cerr << "Failed to stringify!" << t << std::endl;
+- abort();
+- }
+- return out.str();
+-}
+-
+-
+-template <>
+-inline std::string stringify(bool b)
+-{
+- return b ? "true" : "false";
+-}
+-
+-
+-template <typename T>
+-std::string stringify(const std::set<T>& set)
+-{
+- std::ostringstream out;
+- out << "{ ";
+- typename std::set<T>::const_iterator iterator = set.begin();
+- while (iterator != set.end()) {
+- out << stringify(*iterator);
+- if (++iterator != set.end()) {
+- out << ", ";
+- }
+- }
+- out << " }";
+- return out.str();
+-}
+-
+-
+-template <typename T>
+-std::string stringify(const std::list<T>& list)
+-{
+- std::ostringstream out;
+- out << "[ ";
+- typename std::list<T>::const_iterator iterator = list.begin();
+- while (iterator != list.end()) {
+- out << stringify(*iterator);
+- if (++iterator != list.end()) {
+- out << ", ";
+- }
+- }
+- out << " ]";
+- return out.str();
+-}
+-
+-
+-template <typename T>
+-std::string stringify(const std::vector<T>& vector)
+-{
+- std::ostringstream out;
+- out << "[ ";
+- typename std::vector<T>::const_iterator iterator = vector.begin();
+- while (iterator != vector.end()) {
+- out << stringify(*iterator);
+- if (++iterator != vector.end()) {
+- out << ", ";
+- }
+- }
+- out << " ]";
+- return out.str();
+-}
+-
+-
+-template <typename K, typename V>
+-std::string stringify(const std::map<K, V>& map)
+-{
+- std::ostringstream out;
+- out << "{ ";
+- typename std::map<K, V>::const_iterator iterator = map.begin();
+- while (iterator != map.end()) {
+- out << stringify(iterator->first);
+- out << ": ";
+- out << stringify(iterator->second);
+- if (++iterator != map.end()) {
+- out << ", ";
+- }
+- }
+- out << " }";
+- return out.str();
+-}
+-
+-
+-template <typename T>
+-std::string stringify(const hashset<T>& set)
+-{
+- std::ostringstream out;
+- out << "{ ";
+- typename hashset<T>::const_iterator iterator = set.begin();
+- while (iterator != set.end()) {
+- out << stringify(*iterator);
+- if (++iterator != set.end()) {
+- out << ", ";
+- }
+- }
+- out << " }";
+- return out.str();
+-}
+-
+-
+-template <typename K, typename V>
+-std::string stringify(const hashmap<K, V>& map)
+-{
+- std::ostringstream out;
+- out << "{ ";
+- typename hashmap<K, V>::const_iterator iterator = map.begin();
+- while (iterator != map.end()) {
+- out << stringify(iterator->first);
+- out << ": ";
+- out << stringify(iterator->second);
+- if (++iterator != map.end()) {
+- out << ", ";
+- }
+- }
+- out << " }";
+- return out.str();
+-}
+-
+-#endif // __STOUT_STRINGIFY_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/strings.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/strings.hpp
+deleted file mode 100644
+index 46a0a26..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/strings.hpp
++++ /dev/null
+@@ -1,262 +0,0 @@
+-#ifndef __STOUT_STRINGS_HPP__
+-#define __STOUT_STRINGS_HPP__
+-
+-#include <algorithm>
+-#include <string>
+-#include <map>
+-#include <vector>
+-
+-#include "foreach.hpp"
+-#include "format.hpp"
+-#include "stringify.hpp"
+-
+-namespace strings {
+-
+-// Flags indicating how remove should operate.
+-enum Mode {
+- PREFIX,
+- SUFFIX,
+- ANY
+-};
+-
+-
+-inline std::string remove(
+- const std::string& from,
+- const std::string& substring,
+- Mode mode = ANY)
+-{
+- std::string result = from;
+-
+- if (mode == PREFIX) {
+- if (from.find(substring) == 0) {
+- result = from.substr(substring.size());
+- }
+- } else if (mode == SUFFIX) {
+- if (from.rfind(substring) == from.size() - substring.size()) {
+- result = from.substr(0, from.size() - substring.size());
+- }
+- } else {
+- size_t index;
+- while ((index = result.find(substring)) != std::string::npos) {
+- result = result.erase(index, substring.size());
+- }
+- }
+-
+- return result;
+-}
+-
+-
+-inline std::string trim(
+- const std::string& from,
+- const std::string& chars = " \t\n\r")
+-{
+- size_t start = from.find_first_not_of(chars);
+- size_t end = from.find_last_not_of(chars);
+- if (start == std::string::npos) { // Contains only characters in chars.
+- return "";
+- }
+-
+- return from.substr(start, end + 1 - start);
+-}
+-
+-
+-// Replaces all the occurrences of the 'from' string with the 'to' string.
+-inline std::string replace(
+- const std::string& s,
+- const std::string& from,
+- const std::string& to)
+-{
+- std::string result = s;
+- size_t index = 0;
+-
+- if (from.empty()) {
+- return result;
+- }
+-
+- while ((index = result.find(from, index)) != std::string::npos) {
+- result.replace(index, from.length(), to);
+- index += to.length();
+- }
+- return result;
+-}
+-
+-
+-// Tokenizes the string using the delimiters.
+-// Empty tokens will not be included in the result.
+-inline std::vector<std::string> tokenize(
+- const std::string& s,
+- const std::string& delims)
+-{
+- size_t offset = 0;
+- std::vector<std::string> tokens;
+-
+- while (true) {
+- size_t i = s.find_first_not_of(delims, offset);
+- if (std::string::npos == i) {
+- break;
+- }
+-
+- size_t j = s.find_first_of(delims, i);
+- if (std::string::npos == j) {
+- tokens.push_back(s.substr(i));
+- offset = s.length();
+- continue;
+- }
+-
+- tokens.push_back(s.substr(i, j - i));
+- offset = j;
+- }
+- return tokens;
+-}
+-
+-
+-// Splits the string using the provided delimiters.
+-// Empty tokens are allowed in the result.
+-inline std::vector<std::string> split(
+- const std::string& s,
+- const std::string& delims)
+-{
+- std::vector<std::string> tokens;
+- size_t offset = 0;
+- size_t next = 0;
+-
+- while (true) {
+- next = s.find_first_of(delims, offset);
+- if (next == std::string::npos) {
+- tokens.push_back(s.substr(offset));
+- break;
+- }
+-
+- tokens.push_back(s.substr(offset, next - offset));
+- offset = next + 1;
+- }
+- return tokens;
+-}
+-
+-
+-// Returns a map of strings to strings based on calling tokenize
+-// twice. All non-pairs are discarded. For example:
+-//
+-// pairs("foo=1;bar=2;baz;foo=3;bam=1=2", ";&", "=")
+-//
+-// Would return a map with the following:
+-// bar: ["2"]
+-// foo: ["1", "3"]
+-inline std::map<std::string, std::vector<std::string> > pairs(
+- const std::string& s,
+- const std::string& delims1,
+- const std::string& delims2)
+-{
+- std::map<std::string, std::vector<std::string> > result;
+-
+- const std::vector<std::string>& tokens = tokenize(s, delims1);
+- foreach (const std::string& token, tokens) {
+- const std::vector<std::string>& pairs = tokenize(token, delims2);
+- if (pairs.size() == 2) {
+- result[pairs[0]].push_back(pairs[1]);
+- }
+- }
+-
+- return result;
+-}
+-
+-
+-inline std::string join(const std::string& separator,
+- const std::string& s1,
+- const std::string& s2)
+-{
+- return s1 + separator + s2;
+-}
+-
+-
+-inline std::string join(const std::string& separator,
+- const std::string& s1,
+- const std::string& s2,
+- const std::string& s3)
+-{
+- return s1 + separator + s2 + separator + s3;
+-}
+-
+-
+-inline std::string join(const std::string& separator,
+- const std::string& s1,
+- const std::string& s2,
+- const std::string& s4,
+- const std::string& s3)
+-{
+- return s1 + separator + s2 + separator + s3 + separator + s4;
+-}
+-
+-
+-// Use duck-typing to join any iterable.
+-template <typename Iterable>
+-inline std::string join(const std::string& separator, const Iterable& i)
+-{
+- std::string result;
+- typename Iterable::const_iterator iterator = i.begin();
+- while (iterator != i.end()) {
+- result += stringify(*iterator);
+- if (++iterator != i.end()) {
+- result += separator;
+- }
+- }
+- return result;
+-}
+-
+-
+-inline bool checkBracketsMatching(
+- const std::string& s,
+- const char openBracket,
+- const char closeBracket)
+-{
+- int count = 0;
+- for (size_t i = 0; i < s.length(); i++) {
+- if (s[i] == openBracket) {
+- count++;
+- } else if (s[i] == closeBracket) {
+- count--;
+- }
+- if (count < 0) {
+- return false;
+- }
+- }
+- return count == 0;
+-}
+-
+-
+-inline bool startsWith(const std::string& s, const std::string& prefix)
+-{
+- return s.find(prefix) == 0;
+-}
+-
+-
+-inline bool endsWith(const std::string& s, const std::string& suffix)
+-{
+- return s.rfind(suffix) == s.length() - suffix.length();
+-}
+-
+-
+-inline bool contains(const std::string& s, const std::string& substr)
+-{
+- return s.find(substr) != std::string::npos;
+-}
+-
+-
+-inline std::string lower(const std::string& s)
+-{
+- std::string result = s;
+- std::transform(result.begin(), result.end(), result.begin(), ::tolower);
+- return result;
+-}
+-
+-
+-inline std::string upper(const std::string& s)
+-{
+- std::string result = s;
+- std::transform(result.begin(), result.end(), result.begin(), ::toupper);
+- return result;
+-}
+-
+-} // namespaces strings {
+-
+-#endif // __STOUT_STRINGS_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/thread.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/thread.hpp
+deleted file mode 100644
+index c5dbe79..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/thread.hpp
++++ /dev/null
+@@ -1,49 +0,0 @@
+-#ifndef __STOUT_THREAD_HPP__
+-#define __STOUT_THREAD_HPP__
+-
+-#include <pthread.h>
+-#include <stdio.h> // For perror.
+-#include <stdlib.h> // For abort.
+-
+-template <typename T>
+-struct ThreadLocal
+-{
+- ThreadLocal()
+- {
+- if (pthread_key_create(&key, NULL) != 0) {
+- perror("Failed to create thread local, pthread_key_create");
+- abort();
+- }
+- }
+-
+- ThreadLocal<T>& operator = (T* t)
+- {
+- if (pthread_setspecific(key, t) != 0) {
+- perror("Failed to set thread local, pthread_setspecific");
+- abort();
+- }
+- return *this;
+- }
+-
+- operator T* () const
+- {
+- return reinterpret_cast<T*>(pthread_getspecific(key));
+- }
+-
+- T* operator -> () const
+- {
+- return reinterpret_cast<T*>(pthread_getspecific(key));
+- }
+-
+-private:
+- // Not expecting any other operators to be used (and the rest?).
+- bool operator * (const ThreadLocal<T>&) const;
+- bool operator == (const ThreadLocal<T>&) const;
+- bool operator != (const ThreadLocal<T>&) const;
+- bool operator < (const ThreadLocal<T>&) const;
+- bool operator > (const ThreadLocal<T>&) const;
+-
+- pthread_key_t key;
+-};
+-
+-#endif // __STOUT_THREAD_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/try.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/try.hpp
+deleted file mode 100644
+index 787bffd..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/try.hpp
++++ /dev/null
+@@ -1,88 +0,0 @@
+-#ifndef __STOUT_TRY_HPP__
+-#define __STOUT_TRY_HPP__
+-
+-#include <assert.h>
+-#include <stdlib.h> // For abort.
+-
+-#include <iostream>
+-#include <string>
+-
+-
+-template <typename T>
+-class Try
+-{
+-public:
+- static Try<T> some(const T& t)
+- {
+- return Try<T>(SOME, new T(t));
+- }
+-
+- static Try<T> error(const std::string& message)
+- {
+- return Try<T>(ERROR, NULL, message);
+- }
+-
+- Try(const T& _t) : state(SOME), t(new T(_t)) {}
+-
+- Try(const Try<T>& that)
+- {
+- state = that.state;
+- if (that.t != NULL) {
+- t = new T(*that.t);
+- } else {
+- t = NULL;
+- }
+- message = that.message;
+- }
+-
+- ~Try()
+- {
+- delete t;
+- }
+-
+- Try<T>& operator = (const Try<T>& that)
+- {
+- if (this != &that) {
+- delete t;
+- state = that.state;
+- if (that.t != NULL) {
+- t = new T(*that.t);
+- } else {
+- t = NULL;
+- }
+- message = that.message;
+- }
+-
+- return *this;
+- }
+-
+- bool isSome() const { return state == SOME; }
+- bool isError() const { return state == ERROR; }
+-
+- T get() const
+- {
+- if (state != SOME) {
+- std::cerr << "Try::get() but state == ERROR: " << error() << std::endl;
+- abort();
+- }
+- return *t;
+- }
+-
+- std::string error() const { assert(state == ERROR); return message; }
+-
+-private:
+- enum State {
+- SOME,
+- ERROR
+- };
+-
+- Try(State _state, T* _t = NULL, const std::string& _message = "")
+- : state(_state), t(_t), message(_message) {}
+-
+- State state;
+- T* t;
+- std::string message;
+-};
+-
+-
+-#endif // __STOUT_TRY_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/utils.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/utils.hpp
+deleted file mode 100644
+index 0f4bba2..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/utils.hpp
++++ /dev/null
+@@ -1,11 +0,0 @@
+-#ifndef __STOUT_UTILS_HPP__
+-#define __STOUT_UTILS_HPP__
+-
+-namespace utils {
+-
+-template <typename T>
+-T copy(const T& t) { return t; }
+-
+-} // namespace utils {
+-
+-#endif // __STOUT_UTILS_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/include/stout/uuid.hpp b/3rdparty/libprocess/3rdparty/stout/include/stout/uuid.hpp
+deleted file mode 100644
+index c6c290d..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/include/stout/uuid.hpp
++++ /dev/null
+@@ -1,54 +0,0 @@
+-#ifndef __STOUT_UUID_HPP__
+-#define __STOUT_UUID_HPP__
+-
+-#include <assert.h>
+-
+-#include <sstream>
+-#include <string>
+-
+-#include <boost/uuid/uuid.hpp>
+-#include <boost/uuid/uuid_io.hpp>
+-#include <boost/uuid/uuid_generators.hpp>
+-
+-struct UUID : boost::uuids::uuid
+-{
+-public:
+- static UUID random()
+- {
+- return UUID(boost::uuids::random_generator()());
+- }
+-
+- static UUID fromBytes(const std::string& s)
+- {
+- boost::uuids::uuid uuid;
+- memcpy(&uuid, s.data(), s.size());
+- return UUID(uuid);
+- }
+-
+- static UUID fromString(const std::string& s)
+- {
+- boost::uuids::uuid uuid;
+- std::istringstream in(s);
+- in >> uuid;
+- return UUID(uuid);
+- }
+-
+- std::string toBytes() const
+- {
+- assert(sizeof(data) == size());
+- return std::string(reinterpret_cast<const char*>(data), sizeof(data));
+- }
+-
+- std::string toString() const
+- {
+- std::ostringstream out;
+- out << *this;
+- return out.str();
+- }
+-
+-private:
+- explicit UUID(const boost::uuids::uuid& uuid)
+- : boost::uuids::uuid(uuid) {}
+-};
+-
+-#endif // __STOUT_UUID_HPP__
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/bytes_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/bytes_tests.cpp
+deleted file mode 100644
+index 18b2474..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/bytes_tests.cpp
++++ /dev/null
+@@ -1,38 +0,0 @@
+-#include <gtest/gtest.h>
+-
+-#include <gmock/gmock.h>
+-
+-#include <stout/bytes.hpp>
+-#include <stout/gtest.hpp>
+-#include <stout/stringify.hpp>
+-#include <stout/try.hpp>
+-
+-
+-TEST(Stout, Bytes)
+-{
+- Try<Bytes> _1terabyte = Bytes::parse("1TB");
+-
+- EXPECT_SOME_EQ(Terabytes(1), _1terabyte);
+- EXPECT_SOME_EQ(Gigabytes(1024), _1terabyte);
+- EXPECT_SOME_EQ(Megabytes(1024 * 1024), _1terabyte);
+- EXPECT_SOME_EQ(Kilobytes(1024 * 1024 * 1024), _1terabyte);
+- EXPECT_SOME_EQ(Bytes(1024LLU * 1024 * 1024 * 1024), _1terabyte);
+-
+- EXPECT_EQ(Bytes(1024), Kilobytes(1));
+- EXPECT_LT(Bytes(1023), Kilobytes(1));
+- EXPECT_GT(Bytes(1025), Kilobytes(1));
+-
+- EXPECT_NE(Megabytes(1023), Gigabytes(1));
+-
+- EXPECT_EQ("0B", stringify(Bytes()));
+-
+- EXPECT_EQ("1KB", stringify(Kilobytes(1)));
+- EXPECT_EQ("1MB", stringify(Megabytes(1)));
+- EXPECT_EQ("1GB", stringify(Gigabytes(1)));
+- EXPECT_EQ("1TB", stringify(Terabytes(1)));
+-
+- EXPECT_EQ("1023B", stringify(Bytes(1023)));
+- EXPECT_EQ("1023KB", stringify(Kilobytes(1023)));
+- EXPECT_EQ("1023MB", stringify(Megabytes(1023)));
+- EXPECT_EQ("1023GB", stringify(Gigabytes(1023)));
+-}
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/duration_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/duration_tests.cpp
+deleted file mode 100644
+index 4269d3c..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/duration_tests.cpp
++++ /dev/null
+@@ -1,100 +0,0 @@
+-#include <gtest/gtest.h>
+-
+-#include <gmock/gmock.h>
+-
+-#include <stout/duration.hpp>
+-#include <stout/gtest.hpp>
+-#include <stout/stringify.hpp>
+-#include <stout/try.hpp>
+-
+-
+-TEST(DurationTest, Comparison)
+-{
+- EXPECT_EQ(Duration::zero(), Seconds(0));
+- EXPECT_EQ(Minutes(180), Hours(3));
+- EXPECT_EQ(Seconds(10800), Hours(3));
+- EXPECT_EQ(Milliseconds(10800000), Hours(3));
+-
+- EXPECT_EQ(Milliseconds(1), Microseconds(1000));
+- EXPECT_EQ(Milliseconds(1000), Seconds(1));
+-
+- EXPECT_GT(Weeks(1), Days(6));
+-
+- EXPECT_LT(Hours(23), Days(1));
+-
+- EXPECT_LE(Hours(24), Days(1));
+- EXPECT_GE(Hours(24), Days(1));
+-
+- EXPECT_NE(Minutes(59), Hours(1));
+-
+- // Maintains precision for a 100 year duration.
+- EXPECT_GT(Weeks(5217) + Nanoseconds(1), Weeks(5217));
+- EXPECT_LT(Weeks(5217) - Nanoseconds(1), Weeks(5217));
+-}
+-
+-TEST(DurationTest, ParseAndTry)
+-{
+- EXPECT_SOME_EQ(Hours(3), Duration::parse("3hrs"));
+- EXPECT_SOME_EQ(Hours(3) + Minutes(30), Duration::parse("3.5hrs"));
+-
+- EXPECT_SOME_EQ(Nanoseconds(3141592653), Duration::create(3.141592653));
+- // Duration can hold only 9.22337e9 seconds.
+- EXPECT_ERROR(Duration::create(10 * 1e9));
+- EXPECT_ERROR(Duration::create(-10 * 1e9));
+-}
+-
+-TEST(DurationTest, Arithmetic)
+-{
+- Duration d = Seconds(11);
+- d += Seconds(9);
+- EXPECT_EQ(Seconds(20), d);
+-
+- d = Seconds(11);
+- d -= Seconds(21);
+- EXPECT_EQ(Seconds(-10), d);
+-
+- d = Seconds(10);
+- d *= 2;
+- EXPECT_EQ(Seconds(20), d);
+-
+- d = Seconds(10);
+- d /= 2.5;
+- EXPECT_EQ(Seconds(4), d);
+-
+- EXPECT_EQ(Seconds(20), Seconds(11) + Seconds(9));
+- EXPECT_EQ(Seconds(-10), Seconds(11) - Seconds(21));
+- EXPECT_EQ(Duration::create(3.3).get(), Seconds(10) * 0.33);
+- EXPECT_EQ(Duration::create(1.25).get(), Seconds(10) / 8);
+-
+- EXPECT_EQ(Duration::create(Days(11).secs() + 9).get(), Days(11) + Seconds(9));
+-}
+-
+-
+-TEST(DurationTest, OutputFormat)
+-{
+- EXPECT_EQ("1ns", stringify(Nanoseconds(1)));
+- EXPECT_EQ("2ns", stringify(Nanoseconds(2)));
+-
+- // Truncated. Seconds in 15 digits of precision, max of double
+- // type's precise digits.
+- EXPECT_EQ("3.141592653secs",
+- stringify(Duration::create(3.14159265358979).get()));
+- EXPECT_EQ("3140ms", stringify(Duration::create(3.14).get()));
+- EXPECT_EQ("10hrs", stringify(Hours(10)));
+- EXPECT_EQ("-10hrs", stringify(Hours(-10)));
+-
+- // "10days" reads better than "1.42857142857143weeks" so it is
+- // printed out in the lower unit.
+- EXPECT_EQ("10days", stringify(Days(10)));
+- // We go one-level down and it is still not a whole number so we
+- // print it out using the higher unit.
+- EXPECT_EQ("1.1875days", stringify(Days(1) + Hours(4) + Minutes(30)));
+- // "2weeks" reads better than "14days" so we use the higher unit
+- // here.
+- EXPECT_EQ("2weeks", stringify(Days(14)));
+-
+- // Boundary cases.
+- EXPECT_EQ("0ns", stringify(Duration::zero()));
+- EXPECT_EQ("15250.2844524715weeks", stringify(Duration::max()));
+- EXPECT_EQ("-15250.2844524715weeks", stringify(Duration::min()));
+-}
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/error_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/error_tests.cpp
+deleted file mode 100644
+index 75e365e..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/error_tests.cpp
++++ /dev/null
+@@ -1,60 +0,0 @@
+-#include <gtest/gtest.h>
+-
+-#include <gmock/gmock.h>
+-
+-#include <string>
+-
+-#include <stout/error.hpp>
+-#include <stout/option.hpp>
+-#include <stout/result.hpp>
+-#include <stout/try.hpp>
+-
+-using std::string;
+-
+-
+-Error error1()
+-{
+- return Error("Failed to ...");
+-}
+-
+-
+-Try<string> error2()
+-{
+- return Error("Failed to ...");
+-}
+-
+-
+-Try<string> error3(const Try<string>& t)
+-{
+- return t;
+-}
+-
+-
+-Result<string> error4()
+-{
+- return Error("Failed to ...");
+-}
+-
+-
+-Result<string> error5(const Result<string>& r)
+-{
+- return r;
+-}
+-
+-
+-TEST(ErrorTest, Test)
+-{
+- Try<string> t = error1();
+- EXPECT_TRUE(t.isError());
+- t = error2();
+- EXPECT_TRUE(t.isError());
+- t = error3(error1());
+- EXPECT_TRUE(t.isError());
+-
+- Result<string> r = error1();
+- EXPECT_TRUE(r.isError());
+- r = error4();
+- EXPECT_TRUE(r.isError());
+- r = error5(error1());
+- EXPECT_TRUE(r.isError());
+-}
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/flags_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/flags_tests.cpp
+deleted file mode 100644
+index 9af2da1..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/flags_tests.cpp
++++ /dev/null
+@@ -1,382 +0,0 @@
+-#include <gmock/gmock.h>
+-
+-#include <map>
+-#include <string>
+-
+-#include <stout/duration.hpp>
+-#include <stout/flags.hpp>
+-#include <stout/gtest.hpp>
+-#include <stout/none.hpp>
+-#include <stout/nothing.hpp>
+-#include <stout/option.hpp>
+-#include <stout/os.hpp>
+-#include <stout/some.hpp>
+-
+-
+-using namespace flags;
+-
+-class TestFlags : public virtual FlagsBase
+-{
+-public:
+- TestFlags()
+- {
+- add(&TestFlags::name1,
+- "name1",
+- "Set name1",
+- "ben folds");
+-
+- add(&TestFlags::name2,
+- "name2",
+- "Set name2",
+- 42);
+-
+- add(&TestFlags::name3,
+- "name3",
+- "Set name3",
+- false);
+-
+- add(&TestFlags::name4,
+- "name4",
+- "Set name4");
+-
+- add(&TestFlags::name5,
+- "name5",
+- "Set name5");
+- }
+-
+- std::string name1;
+- int name2;
+- bool name3;
+- Option<bool> name4;
+- Option<bool> name5;
+-};
+-
+-
+-TEST(FlagsTest, Load)
+-{
+- TestFlags flags;
+-
+- std::map<std::string, Option<std::string> > values;
+-
+- values["name1"] = Some("billy joel");
+- values["name2"] = Some("43");
+- values["name3"] = Some("false");
+- values["no-name4"] = None();
+- values["name5"] = None();
+-
+- flags.load(values);
+-
+- EXPECT_EQ("billy joel", flags.name1);
+- EXPECT_EQ(43, flags.name2);
+- EXPECT_FALSE(flags.name3);
+- ASSERT_SOME(flags.name4);
+- EXPECT_FALSE(flags.name4.get());
+- ASSERT_SOME(flags.name5);
+- EXPECT_TRUE(flags.name5.get());
+-}
+-
+-
+-TEST(FlagsTest, Add)
+-{
+- Flags<TestFlags> flags;
+-
+- Option<std::string> name6;
+-
+- flags.add(&name6,
+- "name6",
+- "Also set name6");
+-
+- bool name7;
+-
+- flags.add(&name7,
+- "name7",
+- "Also set name7",
+- true);
+-
+- Option<std::string> name8;
+-
+- flags.add(&name8,
+- "name8",
+- "Also set name8");
+-
+- std::map<std::string, Option<std::string> > values;
+-
+- values["name6"] = Some("ben folds");
+- values["no-name7"] = None();
+-
+- flags.load(values);
+-
+- ASSERT_SOME(name6);
+- EXPECT_EQ("ben folds", name6.get());
+-
+- EXPECT_FALSE(name7);
+-
+- ASSERT_TRUE(name8.isNone());
+-}
+-
+-
+-TEST(FlagsTest, Flags)
+-{
+- TestFlags flags;
+-
+- std::map<std::string, Option<std::string> > values;
+-
+- values["name1"] = Some("billy joel");
+- values["name2"] = Some("43");
+- values["name3"] = Some("false");
+- values["no-name4"] = None();
+- values["name5"] = None();
+-
+- flags.load(values);
+-
+- EXPECT_EQ("billy joel", flags.name1);
+- EXPECT_EQ(43, flags.name2);
+- EXPECT_FALSE(flags.name3);
+- ASSERT_SOME(flags.name4);
+- EXPECT_FALSE(flags.name4.get());
+- ASSERT_SOME(flags.name5);
+- EXPECT_TRUE(flags.name5.get());
+-}
+-
+-
+-TEST(FlagsTest, LoadFromEnvironment)
+-{
+- TestFlags flags;
+-
+- os::setenv("FLAGSTEST_name1", "billy joel");
+- os::setenv("FLAGSTEST_name2", "43");
+- os::setenv("FLAGSTEST_no-name3", "");
+- os::setenv("FLAGSTEST_no-name4", "");
+- os::setenv("FLAGSTEST_name5", "");
+-
+- Try<Nothing> load = flags.load("FLAGSTEST_");
+- EXPECT_SOME(load);
+-
+- EXPECT_EQ("billy joel", flags.name1);
+- EXPECT_EQ(43, flags.name2);
+- EXPECT_FALSE(flags.name3);
+- ASSERT_SOME(flags.name4);
+- EXPECT_FALSE(flags.name4.get());
+- ASSERT_SOME(flags.name5);
+- EXPECT_TRUE(flags.name5.get());
+-
+- os::unsetenv("FLAGSTEST_name1");
+- os::unsetenv("FLAGSTEST_name2");
+- os::unsetenv("FLAGSTEST_no-name3");
+- os::unsetenv("FLAGSTEST_no-name4");
+- os::unsetenv("FLAGSTEST_name5");
+-}
+-
+-
+-TEST(FlagsTest, LoadFromCommandLine)
+-{
+- TestFlags flags;
+-
+- int argc = 6;
+- char* argv[argc];
+-
+- argv[0] = (char*) "/path/to/program";
+- argv[1] = (char*) "--name1=billy joel";
+- argv[2] = (char*) "--name2=43";
+- argv[3] = (char*) "--no-name3";
+- argv[4] = (char*) "--no-name4";
+- argv[5] = (char*) "--name5";
+-
+- Try<Nothing> load = flags.load("FLAGSTEST_", argc, argv);
+- EXPECT_SOME(load);
+-
+- EXPECT_EQ("billy joel", flags.name1);
+- EXPECT_EQ(43, flags.name2);
+- EXPECT_FALSE(flags.name3);
+- ASSERT_SOME(flags.name4);
+- EXPECT_FALSE(flags.name4.get());
+- ASSERT_SOME(flags.name5);
+- EXPECT_TRUE(flags.name5.get());
+-}
+-
+-
+-TEST(FlagsTest, LoadFromCommandLineWithNonFlags)
+-{
+- TestFlags flags;
+-
+- int argc = 11;
+- char* argv[argc];
+-
+- argv[0] = (char*) "/path/to/program";
+- argv[1] = (char*) "more";
+- argv[2] = (char*) "--name1=billy joel";
+- argv[3] = (char*) "stuff";
+- argv[4] = (char*) "at";
+- argv[5] = (char*) "--name2=43";
+- argv[6] = (char*) "--no-name3";
+- argv[7] = (char*) "--no-name4";
+- argv[8] = (char*) "--name5";
+- argv[9] = (char*) "the";
+- argv[10] = (char*) "end";
+-
+- Try<Nothing> load = flags.load("FLAGSTEST_", argc, argv);
+- EXPECT_SOME(load);
+-
+- EXPECT_EQ("billy joel", flags.name1);
+- EXPECT_EQ(43, flags.name2);
+- EXPECT_FALSE(flags.name3);
+- ASSERT_SOME(flags.name4);
+- EXPECT_FALSE(flags.name4.get());
+- ASSERT_SOME(flags.name5);
+- EXPECT_TRUE(flags.name5.get());
+-}
+-
+-
+-TEST(FlagsTest, DuplicatesFromEnvironment)
+-{
+- TestFlags flags;
+-
+- os::setenv("FLAGSTEST_name1", "ben folds");
+-
+- int argc = 2;
+- char* argv[argc];
+-
+- argv[0] = (char*) "/path/to/program";
+- argv[1] = (char*) "--name1=billy joel";
+-
+- Try<Nothing> load = flags.load("FLAGSTEST_", argc, argv);
+- EXPECT_ERROR(load);
+-
+- EXPECT_EQ("Duplicate flag 'name1' on command line", load.error());
+-
+- os::unsetenv("FLAGSTEST_name1");
+-}
+-
+-
+-TEST(FlagsTest, DuplicatesFromCommandLine)
+-{
+- TestFlags flags;
+-
+- int argc = 3;
+- char* argv[argc];
+-
+- argv[0] = (char*) "/path/to/program";
+- argv[1] = (char*) "--name1=billy joel";
+- argv[2] = (char*) "--name1=ben folds";
+-
+- Try<Nothing> load = flags.load("FLAGSTEST_", argc, argv);
+- EXPECT_ERROR(load);
+-
+- EXPECT_EQ("Duplicate flag 'name1' on command line", load.error());
+-}
+-
+-
+-TEST(FlagsTest, Errors)
+-{
+- TestFlags flags;
+-
+- int argc = 2;
+- char* argv[argc];
+-
+- argv[0] = (char*) "/path/to/program";
+-
+- // Test an unknown flag.
+- argv[1] = (char*) "--foo";
+-
+- Try<Nothing> load = flags.load("FLAGSTEST_", argc, argv);
+- EXPECT_ERROR(load);
+-
+- EXPECT_EQ("Failed to load unknown flag 'foo'", load.error());
+-
+- // Now try an unknown flag with a value.
+- argv[1] = (char*) "--foo=value";
+-
+- load = flags.load("FLAGSTEST_", argc, argv);
+- EXPECT_ERROR(load);
+-
+- EXPECT_EQ("Failed to load unknown flag 'foo'", load.error());
+-
+- // Now try an unknown flag with a 'no-' prefix.
+- argv[1] = (char*) "--no-foo";
+-
+- load = flags.load("FLAGSTEST_", argc, argv);
+- EXPECT_ERROR(load);
+-
+- EXPECT_EQ("Failed to load unknown flag 'foo' via 'no-foo'", load.error());
+-
+- // Now test a boolean flag using the 'no-' prefix _and_ a value.
+- argv[1] = (char*) "--no-name3=value";
+-
+- load = flags.load("FLAGSTEST_", argc, argv);
+- EXPECT_ERROR(load);
+-
+- EXPECT_EQ("Failed to load boolean flag 'name3' via "
+- "'no-name3' with value 'value'", load.error());
+-
+- // Now test a boolean flag that couldn't be parsed.
+- argv[1] = (char*) "--name3=value";
+-
+- load = flags.load("FLAGSTEST_", argc, argv);
+- EXPECT_ERROR(load);
+-
+- EXPECT_EQ("Failed to load flag 'name3': Failed to load value 'value': "
+- "Expecting a boolean (e.g., true or false)", load.error());
+-
+- // Now test a non-boolean flag without a value.
+- argv[1] = (char*) "--name1";
+-
+- load = flags.load("FLAGSTEST_", argc, argv);
+- EXPECT_ERROR(load);
+-
+- EXPECT_EQ("Failed to load non-boolean flag 'name1': "
+- "Missing value", load.error());
+-
+- // Now test a non-boolean flag using the 'no-' prefix.
+- argv[1] = (char*) "--no-name2";
+-
+- load = flags.load("FLAGSTEST_", argc, argv);
+- EXPECT_ERROR(load);
+-
+- EXPECT_EQ("Failed to load non-boolean flag 'name2' "
+- "via 'no-name2'", load.error());
+-}
+-
+-
+-TEST(FlagsTest, Usage)
+-{
+- TestFlags flags;
+-
+- EXPECT_EQ(
+- " --name1=VALUE Set name1 (default: ben folds)\n"
+- " --name2=VALUE Set name2 (default: 42)\n"
+- " --[no-]name3 Set name3 (default: false)\n"
+- " --[no-]name4 Set name4\n"
+- " --[no-]name5 Set name5\n",
+- flags.usage());
+-}
+-
+-
+-TEST(FlagsTest, Duration)
+-{
+- Flags<TestFlags> flags;
+-
+- Duration name6;
+-
+- flags.add(&name6,
+- "name6",
+- "Amount of time",
+- Milliseconds(100));
+-
+- Option<Duration> name7;
+-
+- flags.add(&name7,
+- "name7",
+- "Also some amount of time");
+-
+- std::map<std::string, Option<std::string> > values;
+-
+- values["name6"] = Some("2mins");
+- values["name7"] = Some("3hrs");
+-
+- flags.load(values);
+-
+- EXPECT_EQ(Minutes(2), name6);
+-
+- ASSERT_SOME(name7);
+- EXPECT_EQ(Hours(3), name7.get());
+-}
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/gzip_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/gzip_tests.cpp
+deleted file mode 100644
+index 13296d8..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/gzip_tests.cpp
++++ /dev/null
+@@ -1,53 +0,0 @@
+-#include <gtest/gtest.h>
+-
+-#include <gmock/gmock.h>
+-
+-#include <string>
+-
+-#include <stout/gtest.hpp>
+-#include <stout/gzip.hpp>
+-
+-using std::string;
+-
+-
+-#ifdef HAVE_LIBZ
+-TEST(GzipTest, CompressDecompressString)
+-{
+- // Test bad compression levels, outside of [-1, Z_BEST_COMPRESSION].
+- ASSERT_ERROR(gzip::compress("", -2));
+- ASSERT_ERROR(gzip::compress("", Z_BEST_COMPRESSION + 1));
+-
+- string s =
+- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
+- "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad "
+- "minim veniam, quis nostrud exercitation ullamco laboris nisi ut "
+- "aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit "
+- "in voluptate velit esse cillum dolore eu fugiat nulla pariatur. "
+- "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui "
+- "officia deserunt mollit anim id est laborum.";
+-
+- Try<string> compressed = gzip::compress(s);
+- ASSERT_SOME(compressed);
+- Try<string> decompressed = gzip::decompress(compressed.get());
+- ASSERT_SOME(decompressed);
+- ASSERT_EQ(s, decompressed.get());
+-
+- // Test with a 1MB random string!
+- s = "";
+- while (s.length() < (1024 * 1024)) {
+- s.append(1, ' ' + (rand() % ('~' - ' ')));
+- }
+- compressed = gzip::compress(s);
+- ASSERT_SOME(compressed);
+- decompressed = gzip::decompress(compressed.get());
+- ASSERT_SOME(decompressed);
+- ASSERT_EQ(s, decompressed.get());
+-
+- s = "";
+- compressed = gzip::compress(s);
+- ASSERT_SOME(compressed);
+- decompressed = gzip::decompress(compressed.get());
+- ASSERT_SOME(decompressed);
+- ASSERT_EQ(s, decompressed.get());
+-}
+-#endif // HAVE_LIBZ
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/hashmap_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/hashmap_tests.cpp
+deleted file mode 100644
+index ff8bafb..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/hashmap_tests.cpp
++++ /dev/null
+@@ -1,25 +0,0 @@
+-#include <gtest/gtest.h>
+-
+-#include <gmock/gmock.h>
+-
+-#include <string>
+-
+-#include <stout/gtest.hpp>
+-#include <stout/hashmap.hpp>
+-
+-using std::string;
+-
+-
+-TEST(HashMapTest, Insert)
+-{
+- hashmap<string, int> map;
+- map["abc"] = 1;
+- map.put("def", 2);
+-
+- ASSERT_SOME_EQ(1, map.get("abc"));
+- ASSERT_SOME_EQ(2, map.get("def"));
+-
+- map.put("def", 4);
+- ASSERT_SOME_EQ(4, map.get("def"));
+- ASSERT_EQ(2, map.size());
+-}
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/hashset_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/hashset_tests.cpp
+deleted file mode 100644
+index 3c4b732..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/hashset_tests.cpp
++++ /dev/null
+@@ -1,48 +0,0 @@
+-#include <gtest/gtest.h>
+-
+-#include <gmock/gmock.h>
+-
+-#include <string>
+-
+-#include <stout/hashset.hpp>
+-
+-using std::string;
+-
+-
+-TEST(HashsetTest, Insert)
+-{
+- hashset<string> hs1;
+- hs1.insert(string("HS1"));
+- hs1.insert(string("HS3"));
+-
+- hashset<string> hs2;
+- hs2.insert(string("HS2"));
+-
+- hs1 = hs2;
+- ASSERT_EQ(1u, hs1.size());
+- ASSERT_TRUE(hs1.contains("HS2"));
+- ASSERT_TRUE(hs1 == hs2);
+-}
+-
+-
+-TEST(HashsetTest, Union)
+-{
+- hashset<int> hs1;
+- hs1.insert(1);
+- hs1.insert(2);
+- hs1.insert(3);
+-
+- hashset<int> hs2;
+- hs2.insert(3);
+- hs2.insert(4);
+- hs2.insert(5);
+-
+- hashset<int> hs3 = hs1 | hs2;
+-
+- ASSERT_EQ(5u, hs3.size());
+- ASSERT_TRUE(hs3.contains(1));
+- ASSERT_TRUE(hs3.contains(2));
+- ASSERT_TRUE(hs3.contains(3));
+- ASSERT_TRUE(hs3.contains(4));
+- ASSERT_TRUE(hs3.contains(5));
+-}
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/json_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/json_tests.cpp
+deleted file mode 100644
+index 29ada8a..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/json_tests.cpp
++++ /dev/null
+@@ -1,33 +0,0 @@
+-#include <gtest/gtest.h>
+-
+-#include <gmock/gmock.h>
+-
+-#include <string>
+-
+-#include <stout/json.hpp>
+-#include <stout/stringify.hpp>
+-
+-using std::string;
+-
+-
+-TEST(JsonTest, BinaryData)
+-{
+- JSON::String s(string("\"\\/\b\f\n\r\t\x00\x19 !#[]\x7F\xFF", 17));
+-
+- EXPECT_EQ("\"\\\"\\\\\\/\\b\\f\\n\\r\\t\\u0000\\u0019 !#[]\\u007F\\u00FF\"",
+- stringify(s));
+-}
+-
+-
+-TEST(JsonTest, NumberFormat)
+-{
+- // Test whole numbers.
+- EXPECT_EQ("0", stringify(JSON::Number(0.0)));
+- EXPECT_EQ("1", stringify(JSON::Number(1.0)));
+-
+- // Negative.
+- EXPECT_EQ("-1", stringify(JSON::Number(-1.0)));
+-
+- // Expect at least 15 digits of precision.
+- EXPECT_EQ("1234567890.12345", stringify(JSON::Number(1234567890.12345)));
+-}
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/linkedhashmap_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/linkedhashmap_tests.cpp
+deleted file mode 100644
+index aca97ca..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/linkedhashmap_tests.cpp
++++ /dev/null
+@@ -1,93 +0,0 @@
+-#include <stdint.h>
+-
+-#include <gtest/gtest.h>
+-
+-#include <list>
+-#include <string>
+-
+-#include <stout/gtest.hpp>
+-#include <stout/linkedhashmap.hpp>
+-
+-using std::list;
+-using std::string;
+-
+-TEST(LinkedHashmapTest, Put)
+-{
+- LinkedHashMap<string, int> map;
+-
+- map["foo"] = 1;
+- ASSERT_SOME_EQ(1, map.get("foo"));
+- ASSERT_EQ(1, map.size());
+-
+- map["bar"] = 2;
+- ASSERT_SOME_EQ(2, map.get("bar"));
+- ASSERT_EQ(2, map.size());
+-
+- map["foo"] = 3;
+- ASSERT_SOME_EQ(3, map.get("foo"));
+- ASSERT_EQ(2, map.size());
+-}
+-
+-
+-TEST(LinkedHashmapTest, Contains)
+-{
+- LinkedHashMap<string, int> map;
+- map["foo"] = 1;
+- map["bar"] = 2;
+- ASSERT_TRUE(map.contains("foo"));
+- ASSERT_TRUE(map.contains("bar"));
+- ASSERT_FALSE(map.contains("caz"));
+-}
+-
+-
+-TEST(LinkedHashmapTest, Erase)
+-{
+- LinkedHashMap<string, int> map;
+-
+- map["foo"] = 1;
+- map["bar"] = 2;
+- ASSERT_EQ(2, map.size());
+-
+- ASSERT_EQ(1, map.erase("foo"));
+- ASSERT_EQ(0, map.erase("caz")); // Non-existent key.
+- ASSERT_NONE(map.get("foo"));
+- ASSERT_EQ(1, map.size());
+- ASSERT_SOME_EQ(2, map.get("bar"));
+-}
+-
+-
+-TEST(LinkedHashmapTest, Keys)
+-{
+- LinkedHashMap<string, int> map;
+-
+- std::list<string> keys;
+- keys.push_back("foo");
+- keys.push_back("bar");
+- keys.push_back("food");
+- keys.push_back("rad");
+- keys.push_back("cat");
+-
+- // Insert keys into the map.
+- foreach (const string& key, keys) {
+- map[key] = 1;
+- }
+- map["foo"] = 1; // Re-insert a key.
+-
+- // Ensure the keys returned are the same as insertion order.
+- ASSERT_EQ(keys, map.keys());
+-}
+-
+-
+-TEST(LinkedHashmapTest, Values)
+-{
+- LinkedHashMap<string, int> map;
+-
+- map["foo"] = 1;
+- map["bar"] = 2;
+- map["caz"] = 3;
+-
+- int val = 0;
+- foreach (int value, map.values()) {
+- ASSERT_EQ(++val, value);
+- }
+-}
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/main.cpp b/3rdparty/libprocess/3rdparty/stout/tests/main.cpp
+deleted file mode 100644
+index 0f1e9cb..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/main.cpp
++++ /dev/null
+@@ -1,11 +0,0 @@
+-#include <gtest/gtest.h>
+-
+-#include <gmock/gmock.h>
+-
+-int main(int argc, char** argv)
+-{
+- // Initialize Google Mock/Test.
+- testing::InitGoogleMock(&argc, argv);
+-
+- return RUN_ALL_TESTS();
+-}
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/multimap_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/multimap_tests.cpp
+deleted file mode 100644
+index 79e7200..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/multimap_tests.cpp
++++ /dev/null
+@@ -1,168 +0,0 @@
+-#include <stdint.h>
+-
+-#include <gtest/gtest.h>
+-
+-#include <set>
+-#include <string>
+-
+-#include <stout/foreach.hpp>
+-#include <stout/multimap.hpp>
+-#include <stout/multihashmap.hpp>
+-
+-using std::set;
+-using std::string;
+-
+-template <typename T>
+-class MultimapTest : public ::testing::Test {};
+-
+-typedef ::testing::Types<
+- Multimap<string, uint16_t>, multihashmap<string, uint16_t> > MultimapTypes;
+-
+-// Causes all TYPED_TEST(MultimapTest, ...) to be run for each of the
+-// specified multimap types.
+-TYPED_TEST_CASE(MultimapTest, MultimapTypes);
+-
+-
+-TYPED_TEST(MultimapTest, Put)
+-{
+- typedef TypeParam Map;
+-
+- Map map;
+-
+- map.put("foo", 1024);
+- ASSERT_EQ(1u, map.get("foo").size());
+-
+- map.put("foo", 1025);
+- ASSERT_EQ(2u, map.get("foo").size());
+-
+- ASSERT_EQ(2u, map.size());
+-
+- map.put("bar", 1024);
+- ASSERT_EQ(1u, map.get("bar").size());
+-
+- map.put("bar", 1025);
+- ASSERT_EQ(2u, map.get("bar").size());
+-
+- ASSERT_EQ(4u, map.size());
+-}
+-
+-
+-TYPED_TEST(MultimapTest, Remove)
+-{
+- typedef TypeParam Map;
+-
+- Map map;
+-
+- map.put("foo", 1024);
+- map.remove("foo", 1024);
+- ASSERT_EQ(0u, map.get("foo").size());
+-
+- ASSERT_EQ(0u, map.size());
+-
+- map.put("foo", 1024);
+- map.put("foo", 1025);
+- ASSERT_EQ(2u, map.get("foo").size());
+-
+- ASSERT_EQ(2u, map.size());
+-
+- map.remove("foo");
+- ASSERT_EQ(0u, map.get("foo").size());
+- ASSERT_EQ(0u, map.size());
+-}
+-
+-
+-TYPED_TEST(MultimapTest, Size)
+-{
+- typedef TypeParam Map;
+-
+- Map map;
+-
+- map.put("foo", 1024);
+- map.put("foo", 1025);
+- ASSERT_EQ(2u, map.get("foo").size());
+- ASSERT_TRUE(map.contains("foo", 1024));
+- ASSERT_TRUE(map.contains("foo", 1025));
+- ASSERT_EQ(2u, map.size());
+-
+- map.put("bar", 1024);
+- map.put("bar", 1025);
+- ASSERT_EQ(2u, map.get("bar").size());
+- ASSERT_TRUE(map.contains("bar", 1024));
+- ASSERT_TRUE(map.contains("bar", 1025));
+- ASSERT_EQ(4u, map.size());
+-}
+-
+-
+-TYPED_TEST(MultimapTest, Keys)
+-{
+- typedef TypeParam Map;
+-
+- Map map;
+-
+- map.put("foo", 1024);
+- map.put("foo", 1024);
+- map.put("foo", 1024);
+- map.put("foo", 1025);
+- map.put("bar", 1);
+-
+- set<string> keys = map.keys();
+-
+- ASSERT_EQ(2, keys.size());
+- ASSERT_EQ(1, keys.count("foo"));
+- ASSERT_EQ(1, keys.count("bar"));
+-}
+-
+-
+-TYPED_TEST(MultimapTest, Iterator)
+-{
+- typedef TypeParam Map;
+-
+- Map map;
+-
+- map.put("foo", 1024);
+- map.put("foo", 1025);
+- ASSERT_EQ(2u, map.get("foo").size());
+- ASSERT_TRUE(map.contains("foo", 1024));
+- ASSERT_TRUE(map.contains("foo", 1025));
+-
+- typename Map::iterator i = map.begin();
+-
+- ASSERT_TRUE(i != map.end());
+-
+- ASSERT_EQ("foo", i->first);
+- ASSERT_EQ(1024, i->second);
+-
+- ++i;
+- ASSERT_TRUE(i != map.end());
+-
+- ASSERT_EQ("foo", i->first);
+- ASSERT_EQ(1025, i->second);
+-
+- ++i;
+- ASSERT_TRUE(i == map.end());
+-}
+-
+-
+-TYPED_TEST(MultimapTest, Foreach)
+-{
+- typedef TypeParam Map;
+-
+- Map map;
+-
+- map.put("foo", 1024);
+- map.put("bar", 1025);
+- ASSERT_EQ(1u, map.get("foo").size());
+- ASSERT_EQ(1u, map.get("bar").size());
+- ASSERT_TRUE(map.contains("foo", 1024));
+- ASSERT_TRUE(map.contains("bar", 1025));
+-
+- foreachpair (const string& key, uint16_t value, map) {
+- if (key == "foo") {
+- ASSERT_EQ(1024, value);
+- } else if (key == "bar") {
+- ASSERT_EQ(1025, value);
+- } else {
+- FAIL() << "Unexpected key/value in multimap";
+- }
+- }
+-}
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/none_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/none_tests.cpp
+deleted file mode 100644
+index 38d25bb..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/none_tests.cpp
++++ /dev/null
+@@ -1,59 +0,0 @@
+-#include <gtest/gtest.h>
+-
+-#include <gmock/gmock.h>
+-
+-#include <string>
+-
+-#include <stout/none.hpp>
+-#include <stout/option.hpp>
+-#include <stout/result.hpp>
+-
+-using std::string;
+-
+-
+-None none1()
+-{
+- return None();
+-}
+-
+-
+-Option<string> none2()
+-{
+- return None();
+-}
+-
+-
+-Option<string> none3(const Option<string>& o)
+-{
+- return o;
+-}
+-
+-
+-Result<string> none4()
+-{
+- return None();
+-}
+-
+-
+-Result<string> none5(const Result<string>& r)
+-{
+- return r;
+-}
+-
+-
+-TEST(NoneTest, Test)
+-{
+- Option<string> o = none1();
+- EXPECT_TRUE(o.isNone());
+- o = none2();
+- EXPECT_TRUE(o.isNone());
+- o = none3(none1());
+- EXPECT_TRUE(o.isNone());
+-
+- Result<string> r = none1();
+- EXPECT_TRUE(r.isNone());
+- r = none4();
+- EXPECT_TRUE(r.isNone());
+- r = none5(none1());
+- EXPECT_TRUE(r.isNone());
+-}
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/os/sendfile_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/os/sendfile_tests.cpp
+deleted file mode 100644
+index 194906e..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/os/sendfile_tests.cpp
++++ /dev/null
+@@ -1,84 +0,0 @@
+-#include <gmock/gmock.h>
+-
+-#include <gtest/gtest.h>
+-
+-#include <stout/gtest.hpp>
+-#include <stout/os.hpp>
+-#include <stout/path.hpp>
+-
+-using std::string;
+-
+-// TODO(bmahler): Extend from OsTest.
+-class OsSendfileTest : public ::testing::Test
+-{
+-public:
+- OsSendfileTest()
+- : LOREM_IPSUM(
+- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
+- "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim "
+- "ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut "
+- "aliquip ex ea commodo consequat. Duis aute irure dolor in "
+- "reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla "
+- "pariatur. Excepteur sint occaecat cupidatat non proident, sunt in "
+- "culpa qui officia deserunt mollit anim id est laborum.") {}
+-
+-protected:
+- virtual void SetUp()
+- {
+- const Try<string>& mkdtemp = os::mkdtemp();
+- ASSERT_SOME(mkdtemp);
+- tmpdir = mkdtemp.get();
+- filename = path::join(mkdtemp.get(), "lorem.txt");
+-
+- ASSERT_SOME(os::write(filename, LOREM_IPSUM));
+- }
+-
+- virtual void TearDown()
+- {
+- ASSERT_SOME(os::rmdir(tmpdir));
+- }
+-
+- const string LOREM_IPSUM;
+- string filename;
+-
+-private:
+- string tmpdir;
+-};
+-
+-
+-TEST_F(OsSendfileTest, sendfile)
+-{
+- Try<int> fd = os::open(filename, O_RDONLY);
+- ASSERT_SOME(fd);
+-
+- // Construct a socket pair and use sendfile to transmit the text.
+- int s[2];
+- ASSERT_NE(-1, socketpair(AF_UNIX, SOCK_STREAM, 0, s)) << strerror(errno);
+- ASSERT_EQ(
+- LOREM_IPSUM.size(),
+- os::sendfile(s[0], fd.get(), 0, LOREM_IPSUM.size()));
+-
+- char* buffer = new char[LOREM_IPSUM.size()];
+- ASSERT_EQ(LOREM_IPSUM.size(), read(s[1], buffer, LOREM_IPSUM.size()));
+- ASSERT_EQ(LOREM_IPSUM, string(buffer, LOREM_IPSUM.size()));
+- ASSERT_SOME(os::close(fd.get()));
+- delete buffer;
+-
+- // Now test with a closed socket, the SIGPIPE should be suppressed!
+- fd = os::open(filename, O_RDONLY);
+- ASSERT_SOME(fd);
+- ASSERT_SOME(os::close(s[1]));
+-
+- ssize_t result = os::sendfile(s[0], fd.get(), 0, LOREM_IPSUM.size());
+- int _errno = errno;
+- ASSERT_EQ(-1, result);
+-
+-#ifdef __linux__
+- ASSERT_EQ(EPIPE, _errno) << strerror(_errno);
+-#elif defined __APPLE__
+- ASSERT_EQ(ENOTCONN, _errno) << strerror(_errno);
+-#endif
+-
+- ASSERT_SOME(os::close(fd.get()));
+- ASSERT_SOME(os::close(s[0]));
+-}
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/os/signals_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/os/signals_tests.cpp
+deleted file mode 100644
+index 66caa04..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/os/signals_tests.cpp
++++ /dev/null
+@@ -1,34 +0,0 @@
+-#include <errno.h>
+-
+-#include <gmock/gmock.h>
+-
+-#include <gtest/gtest.h>
+-
+-#include <stout/gtest.hpp>
+-#include <stout/os.hpp>
+-
+-using std::string;
+-
+-// TODO(bmahler): Expose OsTest so this can use it.
+-class OsSignalsTest : public ::testing::Test {};
+-
+-
+-TEST_F(OsSignalsTest, suppress)
+-{
+- int pipes[2];
+- ASSERT_NE(-1, pipe(pipes));
+-
+- ASSERT_SOME(os::close(pipes[0]));
+-
+- const string data = "hello";
+-
+- // Let's make sure we can suppress SIGPIPE!
+- suppress(SIGPIPE) {
+- // Writing to a pipe that has been closed generates SIGPIPE.
+- ASSERT_EQ(-1, write(pipes[1], data.c_str(), data.length()));
+-
+- ASSERT_EQ(EPIPE, errno);
+- }
+-
+- ASSERT_SOME(os::close(pipes[1]));
+-}
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/os_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/os_tests.cpp
+deleted file mode 100644
+index a0b624b..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/os_tests.cpp
++++ /dev/null
+@@ -1,533 +0,0 @@
+-#include <gmock/gmock.h>
+-
+-#include <gtest/gtest.h>
+-
+-#include <cstdlib> // For rand.
+-#include <list>
+-#include <set>
+-#include <string>
+-
+-#include <stout/duration.hpp>
+-#include <stout/foreach.hpp>
+-#include <stout/gtest.hpp>
+-#include <stout/hashset.hpp>
+-#include <stout/os.hpp>
+-#include <stout/stopwatch.hpp>
+-#include <stout/try.hpp>
+-#include <stout/uuid.hpp>
+-
+-#ifdef __APPLE__
+-#include <stout/os/sysctl.hpp>
+-#endif
+-
+-using os::Exec;
+-using os::Fork;
+-using os::Process;
+-using os::ProcessTree;
+-
+-using std::list;
+-using std::set;
+-using std::string;
+-
+-
+-static hashset<string> listfiles(const string& directory)
+-{
+- hashset<string> fileset;
+- foreach (const string& file, os::ls(directory)) {
+- fileset.insert(file);
+- }
+- return fileset;
+-}
+-
+-
+-class OsTest : public ::testing::Test
+-{
+-protected:
+- virtual void SetUp()
+- {
+- const Try<string>& mkdtemp = os::mkdtemp();
+- ASSERT_SOME(mkdtemp);
+- tmpdir = mkdtemp.get();
+- }
+-
+- virtual void TearDown()
+- {
+- ASSERT_SOME(os::rmdir(tmpdir));
+- }
+-
+- string tmpdir;
+-};
+-
+-
+-TEST_F(OsTest, rmdir)
+-{
+- const hashset<string> EMPTY;
+-
+- hashset<string> expectedListing = EMPTY;
+- EXPECT_EQ(expectedListing, listfiles(tmpdir));
+-
+- os::mkdir(tmpdir + "/a/b/c");
+- os::mkdir(tmpdir + "/a/b/d");
+- os::mkdir(tmpdir + "/e/f");
+-
+- expectedListing = EMPTY;
+- expectedListing.insert("a");
+- expectedListing.insert("e");
+- EXPECT_EQ(expectedListing, listfiles(tmpdir));
+-
+- expectedListing = EMPTY;
+- expectedListing.insert("b");
+- EXPECT_EQ(expectedListing, listfiles(tmpdir + "/a"));
+-
+- expectedListing = EMPTY;
+- expectedListing.insert("c");
+- expectedListing.insert("d");
+- EXPECT_EQ(expectedListing, listfiles(tmpdir + "/a/b"));
+-
+- expectedListing = EMPTY;
+- EXPECT_EQ(expectedListing, listfiles(tmpdir + "/a/b/c"));
+- EXPECT_EQ(expectedListing, listfiles(tmpdir + "/a/b/d"));
+-
+- expectedListing.insert("f");
+- EXPECT_EQ(expectedListing, listfiles(tmpdir + "/e"));
+-
+- expectedListing = EMPTY;
+- EXPECT_EQ(expectedListing, listfiles(tmpdir + "/e/f"));
+-}
+-
+-
+-TEST_F(OsTest, nonblock)
+-{
+- int pipes[2];
+- ASSERT_NE(-1, pipe(pipes));
+-
+- Try<bool> isNonBlock = false;
+-
+- isNonBlock = os::isNonblock(pipes[0]);
+- ASSERT_SOME(isNonBlock);
+- EXPECT_FALSE(isNonBlock.get());
+-
+- ASSERT_SOME(os::nonblock(pipes[0]));
+-
+- isNonBlock = os::isNonblock(pipes[0]);
+- ASSERT_SOME(isNonBlock);
+- EXPECT_TRUE(isNonBlock.get());
+-
+- close(pipes[0]);
+- close(pipes[1]);
+-
+- EXPECT_ERROR(os::nonblock(pipes[0]));
+- EXPECT_ERROR(os::nonblock(pipes[0]));
+-}
+-
+-
+-TEST_F(OsTest, touch)
+-{
+- const string& testfile = tmpdir + "/" + UUID::random().toString();
+-
+- ASSERT_SOME(os::touch(testfile));
+- ASSERT_TRUE(os::exists(testfile));
+-}
+-
+-
+-TEST_F(OsTest, readWriteString)
+-{
+- const string& testfile = tmpdir + "/" + UUID::random().toString();
+- const string& teststr = "line1\nline2";
+-
+- ASSERT_SOME(os::write(testfile, teststr));
+-
+- Try<string> readstr = os::read(testfile);
+-
+- ASSERT_SOME(readstr);
+- EXPECT_EQ(teststr, readstr.get());
+-}
+-
+-
+-TEST_F(OsTest, find)
+-{
+- const string& testdir = tmpdir + "/" + UUID::random().toString();
+- const string& subdir = testdir + "/test1";
+- ASSERT_SOME(os::mkdir(subdir)); // Create the directories.
+-
+- // Now write some files.
+- const string& file1 = testdir + "/file1.txt";
+- const string& file2 = subdir + "/file2.txt";
+- const string& file3 = subdir + "/file3.jpg";
+-
+- ASSERT_SOME(os::touch(file1));
+- ASSERT_SOME(os::touch(file2));
+- ASSERT_SOME(os::touch(file3));
+-
+- // Find "*.txt" files.
+- Try<std::list<string> > result = os::find(testdir, ".txt");
+- ASSERT_SOME(result);
+-
+- hashset<string> files;
+- foreach (const string& file, result.get()) {
+- files.insert(file);
+- }
+-
+- ASSERT_EQ(2u, files.size());
+- ASSERT_TRUE(files.contains(file1));
+- ASSERT_TRUE(files.contains(file2));
+-}
+-
+-
+-TEST_F(OsTest, uname)
+-{
+- const Try<os::UTSInfo>& info = os::uname();
+-
+- ASSERT_SOME(info);
+-#ifdef __linux__
+- EXPECT_EQ(info.get().sysname, "Linux");
+-#endif
+-#ifdef __APPLE__
+- EXPECT_EQ(info.get().sysname, "Darwin");
+-#endif
+-}
+-
+-
+-TEST_F(OsTest, sysname)
+-{
+- const Try<string>& name = os::sysname();
+-
+- ASSERT_SOME(name);
+-#ifdef __linux__
+- EXPECT_EQ(name.get(), "Linux");
+-#endif
+-#ifdef __APPLE__
+- EXPECT_EQ(name.get(), "Darwin");
+-#endif
+-}
+-
+-
+-TEST_F(OsTest, release)
+-{
+- const Try<os::Release>& info = os::release();
+-
+- ASSERT_SOME(info);
+-}
+-
+-
+-TEST_F(OsTest, sleep)
+-{
+- Duration duration = Milliseconds(10);
+- Stopwatch stopwatch;
+- stopwatch.start();
+- ASSERT_SOME(os::sleep(duration));
+- ASSERT_LE(duration, stopwatch.elapsed());
+-
+- ASSERT_ERROR(os::sleep(Milliseconds(-10)));
+-}
+-
+-
+-#ifdef __APPLE__
+-TEST_F(OsTest, sysctl)
+-{
+- Try<os::UTSInfo> uname = os::uname();
+-
+- ASSERT_SOME(uname);
+-
+- Try<string> release = os::sysctl(CTL_KERN, KERN_OSRELEASE).string();
+-
+- ASSERT_SOME(release);
+- EXPECT_EQ(uname.get().release, release.get());
+-
+- Try<string> type = os::sysctl(CTL_KERN, KERN_OSTYPE).string();
+-
+- ASSERT_SOME(type);
+- EXPECT_EQ(uname.get().sysname, type.get());
+-
+- Try<int> maxproc = os::sysctl(CTL_KERN, KERN_MAXPROC).integer();
+-
+- ASSERT_SOME(maxproc);
+-
+- Try<std::vector<kinfo_proc> > processes =
+- os::sysctl(CTL_KERN, KERN_PROC, KERN_PROC_ALL).table(maxproc.get());
+-
+- ASSERT_SOME(processes);
+-
+- std::set<pid_t> pids;
+-
+- foreach (const kinfo_proc& process, processes.get()) {
+- pids.insert(process.kp_proc.p_pid);
+- }
+-
+- EXPECT_EQ(1, pids.count(getpid()));
+-}
+-#endif // __APPLE__
+-
+-
+-TEST_F(OsTest, pids)
+-{
+- Try<set<pid_t> > pids = os::pids();
+- ASSERT_SOME(pids);
+- EXPECT_NE(0u, pids.get().size());
+- EXPECT_EQ(1u, pids.get().count(getpid()));
+- EXPECT_EQ(1u, pids.get().count(1));
+-
+- pids = os::pids(getpgid(0), None());
+- EXPECT_SOME(pids);
+- EXPECT_GE(pids.get().size(), 1u);
+- EXPECT_EQ(1u, pids.get().count(getpid()));
+-
+- EXPECT_ERROR(os::pids(-1, None()));
+-
+- pids = os::pids(None(), getsid(0));
+- EXPECT_SOME(pids);
+- EXPECT_GE(pids.get().size(), 1u);
+- EXPECT_EQ(1u, pids.get().count(getpid()));
+-
+- EXPECT_ERROR(os::pids(None(), -1));
+-}
+-
+-
+-TEST_F(OsTest, children)
+-{
+- Try<set<pid_t> > children = os::children(getpid());
+-
+- ASSERT_SOME(children);
+- EXPECT_EQ(0u, children.get().size());
+-
+- Try<ProcessTree> tree =
+- Fork(None(), // Child.
+- Fork(Exec("sleep 10")), // Grandchild.
+- Exec("sleep 10"))();
+-
+- ASSERT_SOME(tree);
+- ASSERT_EQ(1u, tree.get().children.size());
+-
+- pid_t child = tree.get().process.pid;
+- pid_t grandchild = tree.get().children.front().process.pid;
+-
+- // Ensure the non-recursive children does not include the
+- // grandchild.
+- children = os::children(getpid(), false);
+-
+- ASSERT_SOME(children);
+- EXPECT_EQ(1u, children.get().size());
+- EXPECT_EQ(1u, children.get().count(child));
+-
+- children = os::children(getpid());
+-
+- ASSERT_SOME(children);
+-
+- // Depending on whether or not the shell has fork/exec'ed in each
+- // above 'Exec', we could have 2 or 4 children. That is, some shells
+- // might simply for exec the command above (i.e., 'sleep 10') while
+- // others might fork/exec the command, keeping around a 'sh -c'
+- // process as well.
+- EXPECT_LE(2u, children.get().size());
+- EXPECT_GE(4u, children.get().size());
+-
+- EXPECT_EQ(1u, children.get().count(child));
+- EXPECT_EQ(1u, children.get().count(grandchild));
+-
+- // Cleanup by killing the descendant processes.
+- EXPECT_EQ(0, kill(grandchild, SIGKILL));
+- EXPECT_EQ(0, kill(child, SIGKILL));
+-
+- // We have to reap the child for running the tests in repetition.
+- ASSERT_EQ(child, waitpid(child, NULL, 0));
+-}
+-
+-
+-TEST_F(OsTest, process)
+-{
+- const Result<Process>& process = os::process(getpid());
+-
+- ASSERT_SOME(process);
+- EXPECT_EQ(getpid(), process.get().pid);
+- EXPECT_EQ(getppid(), process.get().parent);
+- ASSERT_SOME(process.get().session);
+- EXPECT_EQ(getsid(getpid()), process.get().session.get());
+-
+- ASSERT_SOME(process.get().rss);
+- EXPECT_GT(process.get().rss.get(), 0);
+-
+- // NOTE: On Linux /proc is a bit slow to update the CPU times,
+- // hence we allow 0 in this test.
+- ASSERT_SOME(process.get().utime);
+- EXPECT_GE(process.get().utime.get(), Nanoseconds(0));
+- ASSERT_SOME(process.get().stime);
+- EXPECT_GE(process.get().stime.get(), Nanoseconds(0));
+-
+- EXPECT_FALSE(process.get().command.empty());
+-}
+-
+-
+-TEST_F(OsTest, processes)
+-{
+- const Try<list<Process> >& processes = os::processes();
+-
+- ASSERT_SOME(processes);
+- ASSERT_GT(processes.get().size(), 2);
+-
+- // Look for ourselves in the table.
+- bool found = false;
+- foreach (const Process& process, processes.get()) {
+- if (process.pid == getpid()) {
+- found = true;
+- EXPECT_EQ(getpid(), process.pid);
+- EXPECT_EQ(getppid(), process.parent);
+- ASSERT_SOME(process.session);
+- EXPECT_EQ(getsid(getpid()), process.session.get());
+-
+- ASSERT_SOME(process.rss);
+- EXPECT_GT(process.rss.get(), 0);
+-
+- // NOTE: On linux /proc is a bit slow to update the cpu times,
+- // hence we allow 0 in this test.
+- ASSERT_SOME(process.utime);
+- EXPECT_GE(process.utime.get(), Nanoseconds(0));
+- ASSERT_SOME(process.stime);
+- EXPECT_GE(process.stime.get(), Nanoseconds(0));
+-
+- EXPECT_FALSE(process.command.empty());
+-
+- break;
+- }
+- }
+-
+- EXPECT_TRUE(found);
+-}
+-
+-
+-void dosetsid(void)
+-{
+- if (::setsid() == -1) {
+- perror("Failed to setsid");
+- abort();
+- }
+-}
+-
+-
+-TEST_F(OsTest, killtree)
+-{
+- Try<ProcessTree> tree =
+- Fork(dosetsid, // Child.
+- Fork(None(), // Grandchild.
+- Fork(None(), // Great-grandchild.
+- Fork(dosetsid, // Great-great-granchild.
+- Exec("sleep 10")),
+- Exec("sleep 10")),
+- Exec("exit 0")),
+- Exec("sleep 10"))();
+-
+- ASSERT_SOME(tree);
+-
+- // The process tree we instantiate initially looks like this:
+- //
+- // -+- child sleep 10
+- // \-+- grandchild exit 0
+- // \-+- greatGrandchild sleep 10
+- // \--- greatGreatGrandchild sleep 10
+- //
+- // But becomes two process trees after the grandchild exits:
+- //
+- // -+- child sleep 10
+- // \--- grandchild (exit 0)
+- //
+- // -+- greatGrandchild sleep 10
+- // \--- greatGreatGrandchild sleep 10
+-
+- // Grab the pids from the instantiated process tree.
+- ASSERT_EQ(1u, tree.get().children.size());
+- ASSERT_EQ(1u, tree.get().children.front().children.size());
+- ASSERT_EQ(1u, tree.get().children.front().children.front().children.size());
+-
+- pid_t child = tree.get();
+- pid_t grandchild = tree.get().children.front();
+- pid_t greatGrandchild = tree.get().children.front().children.front();
+- pid_t greatGreatGrandchild =
+- tree.get().children.front().children.front().children.front();
+-
+- // Now wait for the grandchild to exit splitting the process tree.
+- os::sleep(Milliseconds(50));
+-
+- // Kill the process tree and follow sessions and groups to make sure
+- // we cross the broken link due to the grandchild.
+- Try<std::list<ProcessTree> > trees =
+- os::killtree(child, SIGKILL, true, true);
+-
+- ASSERT_SOME(trees);
+-
+- EXPECT_EQ(2u, trees.get().size()) << stringify(trees.get());
+-
+- foreach (const ProcessTree& tree, trees.get()) {
+- if (tree.process.pid == child) {
+- // The 'grandchild' _might_ still be in the tree, just zombied,
+- // unless the 'child' reaps the 'grandchild', which may happen
+- // if the shell "sticks around" (i.e., some invocations of 'sh
+- // -c' will 'exec' the command which will likely not do any
+- // reaping, but in other cases an invocation of 'sh -c' will not
+- // 'exec' the command, for example when the command is a
+- // sequence of commands separated by ';').
+- EXPECT_FALSE(tree.contains(greatGrandchild)) << tree;
+- EXPECT_FALSE(tree.contains(greatGreatGrandchild)) << tree;
+- } else if (tree.process.pid == greatGrandchild) {
+- EXPECT_TRUE(tree.contains(greatGreatGrandchild)) << tree;
+- } else {
+- FAIL()
+- << "Not expecting a process tree rooted at "
+- << tree.process.pid << "\n" << tree;
+- }
+- }
+-
+- // There is a delay for processes to move into the zombie state.
+- os::sleep(Milliseconds(50));
+-
+- // Expect the pids to be wiped!
+- EXPECT_NONE(os::process(greatGreatGrandchild));
+- EXPECT_NONE(os::process(greatGrandchild));
+- EXPECT_NONE(os::process(grandchild));
+- EXPECT_SOME(os::process(child));
+- EXPECT_TRUE(os::process(child).get().zombie);
+-
+- // We have to reap the child for running the tests in repetition.
+- ASSERT_EQ(child, waitpid(child, NULL, 0));
+-}
+-
+-
+-TEST_F(OsTest, pstree)
+-{
+- Try<ProcessTree> tree = os::pstree(getpid());
+-
+- ASSERT_SOME(tree);
+- EXPECT_EQ(0u, tree.get().children.size()) << stringify(tree.get());
+-
+- tree =
+- Fork(None(), // Child.
+- Fork(Exec("sleep 10")), // Grandchild.
+- Exec("sleep 10"))();
+-
+- ASSERT_SOME(tree);
+-
+- // Depending on whether or not the shell has fork/exec'ed,
+- // we could have 1 or 2 direct children. That is, some shells
+- // might simply exec the command above (i.e., 'sleep 10') while
+- // others might fork/exec the command, keeping around a 'sh -c'
+- // process as well.
+- ASSERT_LE(1u, tree.get().children.size());
+- ASSERT_GE(2u, tree.get().children.size());
+-
+- pid_t child = tree.get().process.pid;
+- pid_t grandchild = tree.get().children.front().process.pid;
+-
+- // Now check pstree again.
+- tree = os::pstree(child);
+-
+- ASSERT_SOME(tree);
+- EXPECT_EQ(child, tree.get().process.pid);
+-
+- ASSERT_LE(1u, tree.get().children.size());
+- ASSERT_GE(2u, tree.get().children.size());
+-
+- // Cleanup by killing the descendant processes.
+- EXPECT_EQ(0, kill(grandchild, SIGKILL));
+- EXPECT_EQ(0, kill(child, SIGKILL));
+-
+- // We have to reap the child for running the tests in repetition.
+- ASSERT_EQ(child, waitpid(child, NULL, 0));
+-}
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/proc_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/proc_tests.cpp
+deleted file mode 100644
+index bc7e248..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/proc_tests.cpp
++++ /dev/null
+@@ -1,54 +0,0 @@
+-#include <unistd.h> // For getpid, getppid.
+-
+-#include <gmock/gmock.h>
+-
+-#include <set>
+-
+-#include <stout/gtest.hpp>
+-#include <stout/proc.hpp>
+-#include <stout/try.hpp>
+-
+-using proc::CPU;
+-using proc::SystemStatus;
+-using proc::ProcessStatus;
+-
+-using std::set;
+-
+-
+-TEST(ProcTest, pids)
+-{
+- Try<set<pid_t> > pids = proc::pids();
+-
+- ASSERT_SOME(pids);
+- EXPECT_NE(0u, pids.get().size());
+- EXPECT_EQ(1u, pids.get().count(getpid()));
+- EXPECT_EQ(1u, pids.get().count(1));
+-}
+-
+-
+-TEST(ProcTest, cpus)
+-{
+- Try<std::list<CPU> > cpus = proc::cpus();
+-
+- ASSERT_SOME(cpus);
+- EXPECT_LE(1u, cpus.get().size());
+-}
+-
+-
+-TEST(ProcTest, SystemStatus)
+-{
+- Try<SystemStatus> status = proc::status();
+-
+- ASSERT_SOME(status);
+- EXPECT_NE(0u, status.get().btime);
+-}
+-
+-
+-TEST(ProcTest, ProcessStatus)
+-{
+- Result<ProcessStatus> status = proc::status(getpid());
+-
+- ASSERT_SOME(status);
+- EXPECT_EQ(getpid(), status.get().pid);
+- EXPECT_EQ(getppid(), status.get().ppid);
+-}
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/protobuf_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/protobuf_tests.cpp
+deleted file mode 100644
+index 02bbf74..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/protobuf_tests.cpp
++++ /dev/null
+@@ -1,95 +0,0 @@
+-#include <gtest/gtest.h>
+-
+-#include <gmock/gmock.h>
+-
+-#include <string>
+-
+-#include <stout/json.hpp>
+-#include <stout/protobuf.hpp>
+-#include <stout/stringify.hpp>
+-#include <stout/strings.hpp>
+-
+-#include "protobuf_tests.pb.h"
+-
+-using std::string;
+-
+-TEST(ProtobufTest, JSON)
+-{
+- tests::Message message;
+- message.set_str("string");
+- message.set_bytes("bytes");
+- message.set_int32(-1);
+- message.set_int64(-1);
+- message.set_uint32(1);
+- message.set_uint64(1);
+- message.set_sint32(-1);
+- message.set_sint64(-1);
+- message.set_f(1.0);
+- message.set_d(1.0);
+- message.set_e(tests::ONE);
+- message.mutable_nested()->set_str("nested");
+- message.add_repeated_string("repeated_string");
+- message.add_repeated_bytes("repeated_bytes");
+- message.add_repeated_int32(-2);
+- message.add_repeated_int64(-2);
+- message.add_repeated_uint32(2);
+- message.add_repeated_uint64(2);
+- message.add_repeated_sint32(-2);
+- message.add_repeated_sint64(-2);
+- message.add_repeated_float(1.0);
+- message.add_repeated_double(1.0);
+- message.add_repeated_double(2.0);
+- message.add_repeated_enum(tests::TWO);
+- message.add_repeated_nested()->set_str("repeated_nested");
+-
+- // TODO(bmahler): To dynamically generate a protobuf message,
+- // see the commented-out code below.
+-// DescriptorProto proto;
+-//
+-// proto.set_name("Message");
+-//
+-// FieldDescriptorProto* field = proto.add_field();
+-// field->set_name("str");
+-// field->set_type(FieldDescriptorProto::TYPE_STRING);
+-//
+-// const Descriptor* descriptor = proto.descriptor();
+-//
+-// DynamicMessageFactory factory;
+-// Message* message = factory.GetPrototype(descriptor);
+-//
+-// Reflection* message.getReflection();
+-
+- // The keys are in alphabetical order.
+- string expected = strings::remove(
+- "{"
+- " \"bytes\": \"bytes\","
+- " \"d\": 1,"
+- " \"e\": \"ONE\","
+- " \"f\": 1,"
+- " \"int32\": -1,"
+- " \"int64\": -1,"
+- " \"nested\": { \"str\": \"nested\"},"
+- " \"repeated_bytes\": [\"repeated_bytes\"],"
+- " \"repeated_double\": [1, 2],"
+- " \"repeated_enum\": [\"TWO\"],"
+- " \"repeated_float\": [1],"
+- " \"repeated_int32\": [-2],"
+- " \"repeated_int64\": [-2],"
+- " \"repeated_nested\": [ { \"str\": \"repeated_nested\" } ],"
+- " \"repeated_sint32\": [-2],"
+- " \"repeated_sint64\": [-2],"
+- " \"repeated_string\": [\"repeated_string\"],"
+- " \"repeated_uint32\": [2],"
+- " \"repeated_uint64\": [2],"
+- " \"sint32\": -1,"
+- " \"sint64\": -1,"
+- " \"str\": \"string\","
+- " \"uint32\": 1,"
+- " \"uint64\": 1"
+- "}",
+- " ");
+-
+- JSON::Object object = JSON::Protobuf(message);
+-
+- EXPECT_EQ(expected, stringify(object));
+-}
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/protobuf_tests.pb.cc b/3rdparty/libprocess/3rdparty/stout/tests/protobuf_tests.pb.cc
+deleted file mode 100644
+index ecf34e1..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/protobuf_tests.pb.cc
++++ /dev/null
+@@ -1,1692 +0,0 @@
+-// Generated by the protocol buffer compiler. DO NOT EDIT!
+-
+-#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
+-#include "protobuf_tests.pb.h"
+-
+-#include <algorithm>
+-
+-#include <google/protobuf/stubs/once.h>
+-#include <google/protobuf/io/coded_stream.h>
+-#include <google/protobuf/wire_format_lite_inl.h>
+-#include <google/protobuf/descriptor.h>
+-#include <google/protobuf/reflection_ops.h>
+-#include <google/protobuf/wire_format.h>
+-// @@protoc_insertion_point(includes)
+-
+-namespace tests {
+-
+-namespace {
+-
+-const ::google::protobuf::Descriptor* Nested_descriptor_ = NULL;
+-const ::google::protobuf::internal::GeneratedMessageReflection*
+- Nested_reflection_ = NULL;
+-const ::google::protobuf::Descriptor* Message_descriptor_ = NULL;
+-const ::google::protobuf::internal::GeneratedMessageReflection*
+- Message_reflection_ = NULL;
+-const ::google::protobuf::EnumDescriptor* Enum_descriptor_ = NULL;
+-
+-} // namespace
+-
+-
+-void protobuf_AssignDesc_protobuf_5ftests_2eproto() {
+- protobuf_AddDesc_protobuf_5ftests_2eproto();
+- const ::google::protobuf::FileDescriptor* file =
+- ::google::protobuf::DescriptorPool::generated_pool()->FindFileByName(
+- "protobuf_tests.proto");
+- GOOGLE_CHECK(file != NULL);
+- Nested_descriptor_ = file->message_type(0);
+- static const int Nested_offsets_[1] = {
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Nested, str_),
+- };
+- Nested_reflection_ =
+- new ::google::protobuf::internal::GeneratedMessageReflection(
+- Nested_descriptor_,
+- Nested::default_instance_,
+- Nested_offsets_,
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Nested, _has_bits_[0]),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Nested, _unknown_fields_),
+- -1,
+- ::google::protobuf::DescriptorPool::generated_pool(),
+- ::google::protobuf::MessageFactory::generated_factory(),
+- sizeof(Nested));
+- Message_descriptor_ = file->message_type(1);
+- static const int Message_offsets_[25] = {
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, str_),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, bytes_),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, int32_),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, int64_),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, uint32_),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, uint64_),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, sint32_),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, sint64_),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, f_),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, d_),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, e_),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, nested_),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, repeated_string_),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, repeated_bytes_),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, repeated_int32_),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, repeated_int64_),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, repeated_uint32_),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, repeated_uint64_),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, repeated_sint32_),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, repeated_sint64_),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, repeated_float_),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, repeated_double_),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, repeated_enum_),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, repeated_nested_),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, empty_),
+- };
+- Message_reflection_ =
+- new ::google::protobuf::internal::GeneratedMessageReflection(
+- Message_descriptor_,
+- Message::default_instance_,
+- Message_offsets_,
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, _has_bits_[0]),
+- GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, _unknown_fields_),
+- -1,
+- ::google::protobuf::DescriptorPool::generated_pool(),
+- ::google::protobuf::MessageFactory::generated_factory(),
+- sizeof(Message));
+- Enum_descriptor_ = file->enum_type(0);
+-}
+-
+-namespace {
+-
+-GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AssignDescriptors_once_);
+-inline void protobuf_AssignDescriptorsOnce() {
+- ::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_,
+- &protobuf_AssignDesc_protobuf_5ftests_2eproto);
+-}
+-
+-void protobuf_RegisterTypes(const ::std::string&) {
+- protobuf_AssignDescriptorsOnce();
+- ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
+- Nested_descriptor_, &Nested::default_instance());
+- ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
+- Message_descriptor_, &Message::default_instance());
+-}
+-
+-} // namespace
+-
+-void protobuf_ShutdownFile_protobuf_5ftests_2eproto() {
+- delete Nested::default_instance_;
+- delete Nested_reflection_;
+- delete Message::default_instance_;
+- delete Message_reflection_;
+-}
+-
+-void protobuf_AddDesc_protobuf_5ftests_2eproto() {
+- static bool already_here = false;
+- if (already_here) return;
+- already_here = true;
+- GOOGLE_PROTOBUF_VERIFY_VERSION;
+-
+- ::google::protobuf::DescriptorPool::InternalAddGeneratedFile(
+- "\n\024protobuf_tests.proto\022\005tests\"\025\n\006Nested\022"
+- "\013\n\003str\030\001 \001(\t\"\241\004\n\007Message\022\013\n\003str\030\001 \002(\t\022\r\n"
+- "\005bytes\030\002 \002(\014\022\r\n\005int32\030\003 \001(\005\022\r\n\005int64\030\004 \001"
+- "(\003\022\016\n\006uint32\030\005 \001(\r\022\016\n\006uint64\030\006 \001(\004\022\016\n\006si"
+- "nt32\030\007 \001(\021\022\016\n\006sint64\030\010 \001(\022\022\t\n\001f\030\t \002(\002\022\t\n"
+- "\001d\030\n \002(\001\022\026\n\001e\030\013 \002(\0162\013.tests.Enum\022\035\n\006nest"
+- "ed\030\014 \002(\0132\r.tests.Nested\022\027\n\017repeated_stri"
+- "ng\030\r \003(\t\022\026\n\016repeated_bytes\030\016 \003(\014\022\026\n\016repe"
+- "ated_int32\030\017 \003(\005\022\026\n\016repeated_int64\030\020 \003(\003"
+- "\022\027\n\017repeated_uint32\030\021 \003(\r\022\027\n\017repeated_ui"
+- "nt64\030\022 \003(\004\022\027\n\017repeated_sint32\030\023 \003(\021\022\027\n\017r"
+- "epeated_sint64\030\024 \003(\022\022\026\n\016repeated_float\030\025"
+- " \003(\002\022\027\n\017repeated_double\030\026 \003(\001\022\"\n\rrepeate"
+- "d_enum\030\027 \003(\0162\013.tests.Enum\022&\n\017repeated_ne"
+- "sted\030\030 \003(\0132\r.tests.Nested\022\r\n\005empty\030\031 \003(\t"
+- "*\030\n\004Enum\022\007\n\003ONE\020\001\022\007\n\003TWO\020\002", 626);
+- ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
+- "protobuf_tests.proto", &protobuf_RegisterTypes);
+- Nested::default_instance_ = new Nested();
+- Message::default_instance_ = new Message();
+- Nested::default_instance_->InitAsDefaultInstance();
+- Message::default_instance_->InitAsDefaultInstance();
+- ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_protobuf_5ftests_2eproto);
+-}
+-
+-// Force AddDescriptors() to be called at static initialization time.
+-struct StaticDescriptorInitializer_protobuf_5ftests_2eproto {
+- StaticDescriptorInitializer_protobuf_5ftests_2eproto() {
+- protobuf_AddDesc_protobuf_5ftests_2eproto();
+- }
+-} static_descriptor_initializer_protobuf_5ftests_2eproto_;
+-
+-const ::google::protobuf::EnumDescriptor* Enum_descriptor() {
+- protobuf_AssignDescriptorsOnce();
+- return Enum_descriptor_;
+-}
+-bool Enum_IsValid(int value) {
+- switch(value) {
+- case 1:
+- case 2:
+- return true;
+- default:
+- return false;
+- }
+-}
+-
+-
+-// ===================================================================
+-
+-#ifndef _MSC_VER
+-const int Nested::kStrFieldNumber;
+-#endif // !_MSC_VER
+-
+-Nested::Nested()
+- : ::google::protobuf::Message() {
+- SharedCtor();
+-}
+-
+-void Nested::InitAsDefaultInstance() {
+-}
+-
+-Nested::Nested(const Nested& from)
+- : ::google::protobuf::Message() {
+- SharedCtor();
+- MergeFrom(from);
+-}
+-
+-void Nested::SharedCtor() {
+- _cached_size_ = 0;
+- str_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString);
+- ::memset(_has_bits_, 0, sizeof(_has_bits_));
+-}
+-
+-Nested::~Nested() {
+- SharedDtor();
+-}
+-
+-void Nested::SharedDtor() {
+- if (str_ != &::google::protobuf::internal::kEmptyString) {
+- delete str_;
+- }
+- if (this != default_instance_) {
+- }
+-}
+-
+-void Nested::SetCachedSize(int size) const {
+- GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+- _cached_size_ = size;
+- GOOGLE_SAFE_CONCURRENT_WRITES_END();
+-}
+-const ::google::protobuf::Descriptor* Nested::descriptor() {
+- protobuf_AssignDescriptorsOnce();
+- return Nested_descriptor_;
+-}
+-
+-const Nested& Nested::default_instance() {
+- if (default_instance_ == NULL) protobuf_AddDesc_protobuf_5ftests_2eproto(); return *default_instance_;
+-}
+-
+-Nested* Nested::default_instance_ = NULL;
+-
+-Nested* Nested::New() const {
+- return new Nested;
+-}
+-
+-void Nested::Clear() {
+- if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+- if (has_str()) {
+- if (str_ != &::google::protobuf::internal::kEmptyString) {
+- str_->clear();
+- }
+- }
+- }
+- ::memset(_has_bits_, 0, sizeof(_has_bits_));
+- mutable_unknown_fields()->Clear();
+-}
+-
+-bool Nested::MergePartialFromCodedStream(
+- ::google::protobuf::io::CodedInputStream* input) {
+-#define DO_(EXPRESSION) if (!(EXPRESSION)) return false
+- ::google::protobuf::uint32 tag;
+- while ((tag = input->ReadTag()) != 0) {
+- switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+- // optional string str = 1;
+- case 1: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+- DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+- input, this->mutable_str()));
+- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+- this->str().data(), this->str().length(),
+- ::google::protobuf::internal::WireFormat::PARSE);
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectAtEnd()) return true;
+- break;
+- }
+-
+- default: {
+- handle_uninterpreted:
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+- return true;
+- }
+- DO_(::google::protobuf::internal::WireFormat::SkipField(
+- input, tag, mutable_unknown_fields()));
+- break;
+- }
+- }
+- }
+- return true;
+-#undef DO_
+-}
+-
+-void Nested::SerializeWithCachedSizes(
+- ::google::protobuf::io::CodedOutputStream* output) const {
+- // optional string str = 1;
+- if (has_str()) {
+- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+- this->str().data(), this->str().length(),
+- ::google::protobuf::internal::WireFormat::SERIALIZE);
+- ::google::protobuf::internal::WireFormatLite::WriteString(
+- 1, this->str(), output);
+- }
+-
+- if (!unknown_fields().empty()) {
+- ::google::protobuf::internal::WireFormat::SerializeUnknownFields(
+- unknown_fields(), output);
+- }
+-}
+-
+-::google::protobuf::uint8* Nested::SerializeWithCachedSizesToArray(
+- ::google::protobuf::uint8* target) const {
+- // optional string str = 1;
+- if (has_str()) {
+- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+- this->str().data(), this->str().length(),
+- ::google::protobuf::internal::WireFormat::SERIALIZE);
+- target =
+- ::google::protobuf::internal::WireFormatLite::WriteStringToArray(
+- 1, this->str(), target);
+- }
+-
+- if (!unknown_fields().empty()) {
+- target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray(
+- unknown_fields(), target);
+- }
+- return target;
+-}
+-
+-int Nested::ByteSize() const {
+- int total_size = 0;
+-
+- if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+- // optional string str = 1;
+- if (has_str()) {
+- total_size += 1 +
+- ::google::protobuf::internal::WireFormatLite::StringSize(
+- this->str());
+- }
+-
+- }
+- if (!unknown_fields().empty()) {
+- total_size +=
+- ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(
+- unknown_fields());
+- }
+- GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+- _cached_size_ = total_size;
+- GOOGLE_SAFE_CONCURRENT_WRITES_END();
+- return total_size;
+-}
+-
+-void Nested::MergeFrom(const ::google::protobuf::Message& from) {
+- GOOGLE_CHECK_NE(&from, this);
+- const Nested* source =
+- ::google::protobuf::internal::dynamic_cast_if_available<const Nested*>(
+- &from);
+- if (source == NULL) {
+- ::google::protobuf::internal::ReflectionOps::Merge(from, this);
+- } else {
+- MergeFrom(*source);
+- }
+-}
+-
+-void Nested::MergeFrom(const Nested& from) {
+- GOOGLE_CHECK_NE(&from, this);
+- if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+- if (from.has_str()) {
+- set_str(from.str());
+- }
+- }
+- mutable_unknown_fields()->MergeFrom(from.unknown_fields());
+-}
+-
+-void Nested::CopyFrom(const ::google::protobuf::Message& from) {
+- if (&from == this) return;
+- Clear();
+- MergeFrom(from);
+-}
+-
+-void Nested::CopyFrom(const Nested& from) {
+- if (&from == this) return;
+- Clear();
+- MergeFrom(from);
+-}
+-
+-bool Nested::IsInitialized() const {
+-
+- return true;
+-}
+-
+-void Nested::Swap(Nested* other) {
+- if (other != this) {
+- std::swap(str_, other->str_);
+- std::swap(_has_bits_[0], other->_has_bits_[0]);
+- _unknown_fields_.Swap(&other->_unknown_fields_);
+- std::swap(_cached_size_, other->_cached_size_);
+- }
+-}
+-
+-::google::protobuf::Metadata Nested::GetMetadata() const {
+- protobuf_AssignDescriptorsOnce();
+- ::google::protobuf::Metadata metadata;
+- metadata.descriptor = Nested_descriptor_;
+- metadata.reflection = Nested_reflection_;
+- return metadata;
+-}
+-
+-
+-// ===================================================================
+-
+-#ifndef _MSC_VER
+-const int Message::kStrFieldNumber;
+-const int Message::kBytesFieldNumber;
+-const int Message::kInt32FieldNumber;
+-const int Message::kInt64FieldNumber;
+-const int Message::kUint32FieldNumber;
+-const int Message::kUint64FieldNumber;
+-const int Message::kSint32FieldNumber;
+-const int Message::kSint64FieldNumber;
+-const int Message::kFFieldNumber;
+-const int Message::kDFieldNumber;
+-const int Message::kEFieldNumber;
+-const int Message::kNestedFieldNumber;
+-const int Message::kRepeatedStringFieldNumber;
+-const int Message::kRepeatedBytesFieldNumber;
+-const int Message::kRepeatedInt32FieldNumber;
+-const int Message::kRepeatedInt64FieldNumber;
+-const int Message::kRepeatedUint32FieldNumber;
+-const int Message::kRepeatedUint64FieldNumber;
+-const int Message::kRepeatedSint32FieldNumber;
+-const int Message::kRepeatedSint64FieldNumber;
+-const int Message::kRepeatedFloatFieldNumber;
+-const int Message::kRepeatedDoubleFieldNumber;
+-const int Message::kRepeatedEnumFieldNumber;
+-const int Message::kRepeatedNestedFieldNumber;
+-const int Message::kEmptyFieldNumber;
+-#endif // !_MSC_VER
+-
+-Message::Message()
+- : ::google::protobuf::Message() {
+- SharedCtor();
+-}
+-
+-void Message::InitAsDefaultInstance() {
+- nested_ = const_cast< ::tests::Nested*>(&::tests::Nested::default_instance());
+-}
+-
+-Message::Message(const Message& from)
+- : ::google::protobuf::Message() {
+- SharedCtor();
+- MergeFrom(from);
+-}
+-
+-void Message::SharedCtor() {
+- _cached_size_ = 0;
+- str_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString);
+- bytes_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString);
+- int32_ = 0;
+- int64_ = GOOGLE_LONGLONG(0);
+- uint32_ = 0u;
+- uint64_ = GOOGLE_ULONGLONG(0);
+- sint32_ = 0;
+- sint64_ = GOOGLE_LONGLONG(0);
+- f_ = 0;
+- d_ = 0;
+- e_ = 1;
+- nested_ = NULL;
+- ::memset(_has_bits_, 0, sizeof(_has_bits_));
+-}
+-
+-Message::~Message() {
+- SharedDtor();
+-}
+-
+-void Message::SharedDtor() {
+- if (str_ != &::google::protobuf::internal::kEmptyString) {
+- delete str_;
+- }
+- if (bytes_ != &::google::protobuf::internal::kEmptyString) {
+- delete bytes_;
+- }
+- if (this != default_instance_) {
+- delete nested_;
+- }
+-}
+-
+-void Message::SetCachedSize(int size) const {
+- GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+- _cached_size_ = size;
+- GOOGLE_SAFE_CONCURRENT_WRITES_END();
+-}
+-const ::google::protobuf::Descriptor* Message::descriptor() {
+- protobuf_AssignDescriptorsOnce();
+- return Message_descriptor_;
+-}
+-
+-const Message& Message::default_instance() {
+- if (default_instance_ == NULL) protobuf_AddDesc_protobuf_5ftests_2eproto(); return *default_instance_;
+-}
+-
+-Message* Message::default_instance_ = NULL;
+-
+-Message* Message::New() const {
+- return new Message;
+-}
+-
+-void Message::Clear() {
+- if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+- if (has_str()) {
+- if (str_ != &::google::protobuf::internal::kEmptyString) {
+- str_->clear();
+- }
+- }
+- if (has_bytes()) {
+- if (bytes_ != &::google::protobuf::internal::kEmptyString) {
+- bytes_->clear();
+- }
+- }
+- int32_ = 0;
+- int64_ = GOOGLE_LONGLONG(0);
+- uint32_ = 0u;
+- uint64_ = GOOGLE_ULONGLONG(0);
+- sint32_ = 0;
+- sint64_ = GOOGLE_LONGLONG(0);
+- }
+- if (_has_bits_[8 / 32] & (0xffu << (8 % 32))) {
+- f_ = 0;
+- d_ = 0;
+- e_ = 1;
+- if (has_nested()) {
+- if (nested_ != NULL) nested_->::tests::Nested::Clear();
+- }
+- }
+- repeated_string_.Clear();
+- repeated_bytes_.Clear();
+- repeated_int32_.Clear();
+- repeated_int64_.Clear();
+- repeated_uint32_.Clear();
+- repeated_uint64_.Clear();
+- repeated_sint32_.Clear();
+- repeated_sint64_.Clear();
+- repeated_float_.Clear();
+- repeated_double_.Clear();
+- repeated_enum_.Clear();
+- repeated_nested_.Clear();
+- empty_.Clear();
+- ::memset(_has_bits_, 0, sizeof(_has_bits_));
+- mutable_unknown_fields()->Clear();
+-}
+-
+-bool Message::MergePartialFromCodedStream(
+- ::google::protobuf::io::CodedInputStream* input) {
+-#define DO_(EXPRESSION) if (!(EXPRESSION)) return false
+- ::google::protobuf::uint32 tag;
+- while ((tag = input->ReadTag()) != 0) {
+- switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
+- // required string str = 1;
+- case 1: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+- DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+- input, this->mutable_str()));
+- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+- this->str().data(), this->str().length(),
+- ::google::protobuf::internal::WireFormat::PARSE);
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(18)) goto parse_bytes;
+- break;
+- }
+-
+- // required bytes bytes = 2;
+- case 2: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+- parse_bytes:
+- DO_(::google::protobuf::internal::WireFormatLite::ReadBytes(
+- input, this->mutable_bytes()));
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(24)) goto parse_int32;
+- break;
+- }
+-
+- // optional int32 int32 = 3;
+- case 3: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+- parse_int32:
+- DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+- ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+- input, &int32_)));
+- set_has_int32();
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(32)) goto parse_int64;
+- break;
+- }
+-
+- // optional int64 int64 = 4;
+- case 4: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+- parse_int64:
+- DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+- ::google::protobuf::int64, ::google::protobuf::internal::WireFormatLite::TYPE_INT64>(
+- input, &int64_)));
+- set_has_int64();
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(40)) goto parse_uint32;
+- break;
+- }
+-
+- // optional uint32 uint32 = 5;
+- case 5: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+- parse_uint32:
+- DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+- ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+- input, &uint32_)));
+- set_has_uint32();
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(48)) goto parse_uint64;
+- break;
+- }
+-
+- // optional uint64 uint64 = 6;
+- case 6: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+- parse_uint64:
+- DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+- ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+- input, &uint64_)));
+- set_has_uint64();
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(56)) goto parse_sint32;
+- break;
+- }
+-
+- // optional sint32 sint32 = 7;
+- case 7: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+- parse_sint32:
+- DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+- ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_SINT32>(
+- input, &sint32_)));
+- set_has_sint32();
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(64)) goto parse_sint64;
+- break;
+- }
+-
+- // optional sint64 sint64 = 8;
+- case 8: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+- parse_sint64:
+- DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+- ::google::protobuf::int64, ::google::protobuf::internal::WireFormatLite::TYPE_SINT64>(
+- input, &sint64_)));
+- set_has_sint64();
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(77)) goto parse_f;
+- break;
+- }
+-
+- // required float f = 9;
+- case 9: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_FIXED32) {
+- parse_f:
+- DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+- float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+- input, &f_)));
+- set_has_f();
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(81)) goto parse_d;
+- break;
+- }
+-
+- // required double d = 10;
+- case 10: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_FIXED64) {
+- parse_d:
+- DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+- double, ::google::protobuf::internal::WireFormatLite::TYPE_DOUBLE>(
+- input, &d_)));
+- set_has_d();
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(88)) goto parse_e;
+- break;
+- }
+-
+- // required .tests.Enum e = 11;
+- case 11: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+- parse_e:
+- int value;
+- DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+- int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>(
+- input, &value)));
+- if (tests::Enum_IsValid(value)) {
+- set_e(static_cast< tests::Enum >(value));
+- } else {
+- mutable_unknown_fields()->AddVarint(11, value);
+- }
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(98)) goto parse_nested;
+- break;
+- }
+-
+- // required .tests.Nested nested = 12;
+- case 12: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+- parse_nested:
+- DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+- input, mutable_nested()));
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(106)) goto parse_repeated_string;
+- break;
+- }
+-
+- // repeated string repeated_string = 13;
+- case 13: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+- parse_repeated_string:
+- DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+- input, this->add_repeated_string()));
+- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+- this->repeated_string(0).data(), this->repeated_string(0).length(),
+- ::google::protobuf::internal::WireFormat::PARSE);
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(106)) goto parse_repeated_string;
+- if (input->ExpectTag(114)) goto parse_repeated_bytes;
+- break;
+- }
+-
+- // repeated bytes repeated_bytes = 14;
+- case 14: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+- parse_repeated_bytes:
+- DO_(::google::protobuf::internal::WireFormatLite::ReadBytes(
+- input, this->add_repeated_bytes()));
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(114)) goto parse_repeated_bytes;
+- if (input->ExpectTag(120)) goto parse_repeated_int32;
+- break;
+- }
+-
+- // repeated int32 repeated_int32 = 15;
+- case 15: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+- parse_repeated_int32:
+- DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitive<
+- ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+- 1, 120, input, this->mutable_repeated_int32())));
+- } else if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag)
+- == ::google::protobuf::internal::WireFormatLite::
+- WIRETYPE_LENGTH_DELIMITED) {
+- DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitiveNoInline<
+- ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
+- input, this->mutable_repeated_int32())));
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(120)) goto parse_repeated_int32;
+- if (input->ExpectTag(128)) goto parse_repeated_int64;
+- break;
+- }
+-
+- // repeated int64 repeated_int64 = 16;
+- case 16: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+- parse_repeated_int64:
+- DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitive<
+- ::google::protobuf::int64, ::google::protobuf::internal::WireFormatLite::TYPE_INT64>(
+- 2, 128, input, this->mutable_repeated_int64())));
+- } else if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag)
+- == ::google::protobuf::internal::WireFormatLite::
+- WIRETYPE_LENGTH_DELIMITED) {
+- DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitiveNoInline<
+- ::google::protobuf::int64, ::google::protobuf::internal::WireFormatLite::TYPE_INT64>(
+- input, this->mutable_repeated_int64())));
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(128)) goto parse_repeated_int64;
+- if (input->ExpectTag(136)) goto parse_repeated_uint32;
+- break;
+- }
+-
+- // repeated uint32 repeated_uint32 = 17;
+- case 17: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+- parse_repeated_uint32:
+- DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitive<
+- ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+- 2, 136, input, this->mutable_repeated_uint32())));
+- } else if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag)
+- == ::google::protobuf::internal::WireFormatLite::
+- WIRETYPE_LENGTH_DELIMITED) {
+- DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitiveNoInline<
+- ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
+- input, this->mutable_repeated_uint32())));
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(136)) goto parse_repeated_uint32;
+- if (input->ExpectTag(144)) goto parse_repeated_uint64;
+- break;
+- }
+-
+- // repeated uint64 repeated_uint64 = 18;
+- case 18: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+- parse_repeated_uint64:
+- DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitive<
+- ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+- 2, 144, input, this->mutable_repeated_uint64())));
+- } else if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag)
+- == ::google::protobuf::internal::WireFormatLite::
+- WIRETYPE_LENGTH_DELIMITED) {
+- DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitiveNoInline<
+- ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
+- input, this->mutable_repeated_uint64())));
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(144)) goto parse_repeated_uint64;
+- if (input->ExpectTag(152)) goto parse_repeated_sint32;
+- break;
+- }
+-
+- // repeated sint32 repeated_sint32 = 19;
+- case 19: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+- parse_repeated_sint32:
+- DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitive<
+- ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_SINT32>(
+- 2, 152, input, this->mutable_repeated_sint32())));
+- } else if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag)
+- == ::google::protobuf::internal::WireFormatLite::
+- WIRETYPE_LENGTH_DELIMITED) {
+- DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitiveNoInline<
+- ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_SINT32>(
+- input, this->mutable_repeated_sint32())));
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(152)) goto parse_repeated_sint32;
+- if (input->ExpectTag(160)) goto parse_repeated_sint64;
+- break;
+- }
+-
+- // repeated sint64 repeated_sint64 = 20;
+- case 20: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+- parse_repeated_sint64:
+- DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitive<
+- ::google::protobuf::int64, ::google::protobuf::internal::WireFormatLite::TYPE_SINT64>(
+- 2, 160, input, this->mutable_repeated_sint64())));
+- } else if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag)
+- == ::google::protobuf::internal::WireFormatLite::
+- WIRETYPE_LENGTH_DELIMITED) {
+- DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitiveNoInline<
+- ::google::protobuf::int64, ::google::protobuf::internal::WireFormatLite::TYPE_SINT64>(
+- input, this->mutable_repeated_sint64())));
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(160)) goto parse_repeated_sint64;
+- if (input->ExpectTag(173)) goto parse_repeated_float;
+- break;
+- }
+-
+- // repeated float repeated_float = 21;
+- case 21: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_FIXED32) {
+- parse_repeated_float:
+- DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitive<
+- float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+- 2, 173, input, this->mutable_repeated_float())));
+- } else if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag)
+- == ::google::protobuf::internal::WireFormatLite::
+- WIRETYPE_LENGTH_DELIMITED) {
+- DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitiveNoInline<
+- float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
+- input, this->mutable_repeated_float())));
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(173)) goto parse_repeated_float;
+- if (input->ExpectTag(177)) goto parse_repeated_double;
+- break;
+- }
+-
+- // repeated double repeated_double = 22;
+- case 22: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_FIXED64) {
+- parse_repeated_double:
+- DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitive<
+- double, ::google::protobuf::internal::WireFormatLite::TYPE_DOUBLE>(
+- 2, 177, input, this->mutable_repeated_double())));
+- } else if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag)
+- == ::google::protobuf::internal::WireFormatLite::
+- WIRETYPE_LENGTH_DELIMITED) {
+- DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitiveNoInline<
+- double, ::google::protobuf::internal::WireFormatLite::TYPE_DOUBLE>(
+- input, this->mutable_repeated_double())));
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(177)) goto parse_repeated_double;
+- if (input->ExpectTag(184)) goto parse_repeated_enum;
+- break;
+- }
+-
+- // repeated .tests.Enum repeated_enum = 23;
+- case 23: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
+- parse_repeated_enum:
+- int value;
+- DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
+- int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>(
+- input, &value)));
+- if (tests::Enum_IsValid(value)) {
+- add_repeated_enum(static_cast< tests::Enum >(value));
+- } else {
+- mutable_unknown_fields()->AddVarint(23, value);
+- }
+- } else if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag)
+- == ::google::protobuf::internal::WireFormatLite::
+- WIRETYPE_LENGTH_DELIMITED) {
+- DO_((::google::protobuf::internal::WireFormatLite::ReadPackedEnumNoInline(
+- input,
+- &tests::Enum_IsValid,
+- this->mutable_repeated_enum())));
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(184)) goto parse_repeated_enum;
+- if (input->ExpectTag(194)) goto parse_repeated_nested;
+- break;
+- }
+-
+- // repeated .tests.Nested repeated_nested = 24;
+- case 24: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+- parse_repeated_nested:
+- DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
+- input, add_repeated_nested()));
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(194)) goto parse_repeated_nested;
+- if (input->ExpectTag(202)) goto parse_empty;
+- break;
+- }
+-
+- // repeated string empty = 25;
+- case 25: {
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
+- parse_empty:
+- DO_(::google::protobuf::internal::WireFormatLite::ReadString(
+- input, this->add_empty()));
+- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+- this->empty(0).data(), this->empty(0).length(),
+- ::google::protobuf::internal::WireFormat::PARSE);
+- } else {
+- goto handle_uninterpreted;
+- }
+- if (input->ExpectTag(202)) goto parse_empty;
+- if (input->ExpectAtEnd()) return true;
+- break;
+- }
+-
+- default: {
+- handle_uninterpreted:
+- if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
+- ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
+- return true;
+- }
+- DO_(::google::protobuf::internal::WireFormat::SkipField(
+- input, tag, mutable_unknown_fields()));
+- break;
+- }
+- }
+- }
+- return true;
+-#undef DO_
+-}
+-
+-void Message::SerializeWithCachedSizes(
+- ::google::protobuf::io::CodedOutputStream* output) const {
+- // required string str = 1;
+- if (has_str()) {
+- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+- this->str().data(), this->str().length(),
+- ::google::protobuf::internal::WireFormat::SERIALIZE);
+- ::google::protobuf::internal::WireFormatLite::WriteString(
+- 1, this->str(), output);
+- }
+-
+- // required bytes bytes = 2;
+- if (has_bytes()) {
+- ::google::protobuf::internal::WireFormatLite::WriteBytes(
+- 2, this->bytes(), output);
+- }
+-
+- // optional int32 int32 = 3;
+- if (has_int32()) {
+- ::google::protobuf::internal::WireFormatLite::WriteInt32(3, this->int32(), output);
+- }
+-
+- // optional int64 int64 = 4;
+- if (has_int64()) {
+- ::google::protobuf::internal::WireFormatLite::WriteInt64(4, this->int64(), output);
+- }
+-
+- // optional uint32 uint32 = 5;
+- if (has_uint32()) {
+- ::google::protobuf::internal::WireFormatLite::WriteUInt32(5, this->uint32(), output);
+- }
+-
+- // optional uint64 uint64 = 6;
+- if (has_uint64()) {
+- ::google::protobuf::internal::WireFormatLite::WriteUInt64(6, this->uint64(), output);
+- }
+-
+- // optional sint32 sint32 = 7;
+- if (has_sint32()) {
+- ::google::protobuf::internal::WireFormatLite::WriteSInt32(7, this->sint32(), output);
+- }
+-
+- // optional sint64 sint64 = 8;
+- if (has_sint64()) {
+- ::google::protobuf::internal::WireFormatLite::WriteSInt64(8, this->sint64(), output);
+- }
+-
+- // required float f = 9;
+- if (has_f()) {
+- ::google::protobuf::internal::WireFormatLite::WriteFloat(9, this->f(), output);
+- }
+-
+- // required double d = 10;
+- if (has_d()) {
+- ::google::protobuf::internal::WireFormatLite::WriteDouble(10, this->d(), output);
+- }
+-
+- // required .tests.Enum e = 11;
+- if (has_e()) {
+- ::google::protobuf::internal::WireFormatLite::WriteEnum(
+- 11, this->e(), output);
+- }
+-
+- // required .tests.Nested nested = 12;
+- if (has_nested()) {
+- ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray(
+- 12, this->nested(), output);
+- }
+-
+- // repeated string repeated_string = 13;
+- for (int i = 0; i < this->repeated_string_size(); i++) {
+- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+- this->repeated_string(i).data(), this->repeated_string(i).length(),
+- ::google::protobuf::internal::WireFormat::SERIALIZE);
+- ::google::protobuf::internal::WireFormatLite::WriteString(
+- 13, this->repeated_string(i), output);
+- }
+-
+- // repeated bytes repeated_bytes = 14;
+- for (int i = 0; i < this->repeated_bytes_size(); i++) {
+- ::google::protobuf::internal::WireFormatLite::WriteBytes(
+- 14, this->repeated_bytes(i), output);
+- }
+-
+- // repeated int32 repeated_int32 = 15;
+- for (int i = 0; i < this->repeated_int32_size(); i++) {
+- ::google::protobuf::internal::WireFormatLite::WriteInt32(
+- 15, this->repeated_int32(i), output);
+- }
+-
+- // repeated int64 repeated_int64 = 16;
+- for (int i = 0; i < this->repeated_int64_size(); i++) {
+- ::google::protobuf::internal::WireFormatLite::WriteInt64(
+- 16, this->repeated_int64(i), output);
+- }
+-
+- // repeated uint32 repeated_uint32 = 17;
+- for (int i = 0; i < this->repeated_uint32_size(); i++) {
+- ::google::protobuf::internal::WireFormatLite::WriteUInt32(
+- 17, this->repeated_uint32(i), output);
+- }
+-
+- // repeated uint64 repeated_uint64 = 18;
+- for (int i = 0; i < this->repeated_uint64_size(); i++) {
+- ::google::protobuf::internal::WireFormatLite::WriteUInt64(
+- 18, this->repeated_uint64(i), output);
+- }
+-
+- // repeated sint32 repeated_sint32 = 19;
+- for (int i = 0; i < this->repeated_sint32_size(); i++) {
+- ::google::protobuf::internal::WireFormatLite::WriteSInt32(
+- 19, this->repeated_sint32(i), output);
+- }
+-
+- // repeated sint64 repeated_sint64 = 20;
+- for (int i = 0; i < this->repeated_sint64_size(); i++) {
+- ::google::protobuf::internal::WireFormatLite::WriteSInt64(
+- 20, this->repeated_sint64(i), output);
+- }
+-
+- // repeated float repeated_float = 21;
+- for (int i = 0; i < this->repeated_float_size(); i++) {
+- ::google::protobuf::internal::WireFormatLite::WriteFloat(
+- 21, this->repeated_float(i), output);
+- }
+-
+- // repeated double repeated_double = 22;
+- for (int i = 0; i < this->repeated_double_size(); i++) {
+- ::google::protobuf::internal::WireFormatLite::WriteDouble(
+- 22, this->repeated_double(i), output);
+- }
+-
+- // repeated .tests.Enum repeated_enum = 23;
+- for (int i = 0; i < this->repeated_enum_size(); i++) {
+- ::google::protobuf::internal::WireFormatLite::WriteEnum(
+- 23, this->repeated_enum(i), output);
+- }
+-
+- // repeated .tests.Nested repeated_nested = 24;
+- for (int i = 0; i < this->repeated_nested_size(); i++) {
+- ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray(
+- 24, this->repeated_nested(i), output);
+- }
+-
+- // repeated string empty = 25;
+- for (int i = 0; i < this->empty_size(); i++) {
+- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+- this->empty(i).data(), this->empty(i).length(),
+- ::google::protobuf::internal::WireFormat::SERIALIZE);
+- ::google::protobuf::internal::WireFormatLite::WriteString(
+- 25, this->empty(i), output);
+- }
+-
+- if (!unknown_fields().empty()) {
+- ::google::protobuf::internal::WireFormat::SerializeUnknownFields(
+- unknown_fields(), output);
+- }
+-}
+-
+-::google::protobuf::uint8* Message::SerializeWithCachedSizesToArray(
+- ::google::protobuf::uint8* target) const {
+- // required string str = 1;
+- if (has_str()) {
+- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+- this->str().data(), this->str().length(),
+- ::google::protobuf::internal::WireFormat::SERIALIZE);
+- target =
+- ::google::protobuf::internal::WireFormatLite::WriteStringToArray(
+- 1, this->str(), target);
+- }
+-
+- // required bytes bytes = 2;
+- if (has_bytes()) {
+- target =
+- ::google::protobuf::internal::WireFormatLite::WriteBytesToArray(
+- 2, this->bytes(), target);
+- }
+-
+- // optional int32 int32 = 3;
+- if (has_int32()) {
+- target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(3, this->int32(), target);
+- }
+-
+- // optional int64 int64 = 4;
+- if (has_int64()) {
+- target = ::google::protobuf::internal::WireFormatLite::WriteInt64ToArray(4, this->int64(), target);
+- }
+-
+- // optional uint32 uint32 = 5;
+- if (has_uint32()) {
+- target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(5, this->uint32(), target);
+- }
+-
+- // optional uint64 uint64 = 6;
+- if (has_uint64()) {
+- target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(6, this->uint64(), target);
+- }
+-
+- // optional sint32 sint32 = 7;
+- if (has_sint32()) {
+- target = ::google::protobuf::internal::WireFormatLite::WriteSInt32ToArray(7, this->sint32(), target);
+- }
+-
+- // optional sint64 sint64 = 8;
+- if (has_sint64()) {
+- target = ::google::protobuf::internal::WireFormatLite::WriteSInt64ToArray(8, this->sint64(), target);
+- }
+-
+- // required float f = 9;
+- if (has_f()) {
+- target = ::google::protobuf::internal::WireFormatLite::WriteFloatToArray(9, this->f(), target);
+- }
+-
+- // required double d = 10;
+- if (has_d()) {
+- target = ::google::protobuf::internal::WireFormatLite::WriteDoubleToArray(10, this->d(), target);
+- }
+-
+- // required .tests.Enum e = 11;
+- if (has_e()) {
+- target = ::google::protobuf::internal::WireFormatLite::WriteEnumToArray(
+- 11, this->e(), target);
+- }
+-
+- // required .tests.Nested nested = 12;
+- if (has_nested()) {
+- target = ::google::protobuf::internal::WireFormatLite::
+- WriteMessageNoVirtualToArray(
+- 12, this->nested(), target);
+- }
+-
+- // repeated string repeated_string = 13;
+- for (int i = 0; i < this->repeated_string_size(); i++) {
+- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+- this->repeated_string(i).data(), this->repeated_string(i).length(),
+- ::google::protobuf::internal::WireFormat::SERIALIZE);
+- target = ::google::protobuf::internal::WireFormatLite::
+- WriteStringToArray(13, this->repeated_string(i), target);
+- }
+-
+- // repeated bytes repeated_bytes = 14;
+- for (int i = 0; i < this->repeated_bytes_size(); i++) {
+- target = ::google::protobuf::internal::WireFormatLite::
+- WriteBytesToArray(14, this->repeated_bytes(i), target);
+- }
+-
+- // repeated int32 repeated_int32 = 15;
+- for (int i = 0; i < this->repeated_int32_size(); i++) {
+- target = ::google::protobuf::internal::WireFormatLite::
+- WriteInt32ToArray(15, this->repeated_int32(i), target);
+- }
+-
+- // repeated int64 repeated_int64 = 16;
+- for (int i = 0; i < this->repeated_int64_size(); i++) {
+- target = ::google::protobuf::internal::WireFormatLite::
+- WriteInt64ToArray(16, this->repeated_int64(i), target);
+- }
+-
+- // repeated uint32 repeated_uint32 = 17;
+- for (int i = 0; i < this->repeated_uint32_size(); i++) {
+- target = ::google::protobuf::internal::WireFormatLite::
+- WriteUInt32ToArray(17, this->repeated_uint32(i), target);
+- }
+-
+- // repeated uint64 repeated_uint64 = 18;
+- for (int i = 0; i < this->repeated_uint64_size(); i++) {
+- target = ::google::protobuf::internal::WireFormatLite::
+- WriteUInt64ToArray(18, this->repeated_uint64(i), target);
+- }
+-
+- // repeated sint32 repeated_sint32 = 19;
+- for (int i = 0; i < this->repeated_sint32_size(); i++) {
+- target = ::google::protobuf::internal::WireFormatLite::
+- WriteSInt32ToArray(19, this->repeated_sint32(i), target);
+- }
+-
+- // repeated sint64 repeated_sint64 = 20;
+- for (int i = 0; i < this->repeated_sint64_size(); i++) {
+- target = ::google::protobuf::internal::WireFormatLite::
+- WriteSInt64ToArray(20, this->repeated_sint64(i), target);
+- }
+-
+- // repeated float repeated_float = 21;
+- for (int i = 0; i < this->repeated_float_size(); i++) {
+- target = ::google::protobuf::internal::WireFormatLite::
+- WriteFloatToArray(21, this->repeated_float(i), target);
+- }
+-
+- // repeated double repeated_double = 22;
+- for (int i = 0; i < this->repeated_double_size(); i++) {
+- target = ::google::protobuf::internal::WireFormatLite::
+- WriteDoubleToArray(22, this->repeated_double(i), target);
+- }
+-
+- // repeated .tests.Enum repeated_enum = 23;
+- for (int i = 0; i < this->repeated_enum_size(); i++) {
+- target = ::google::protobuf::internal::WireFormatLite::WriteEnumToArray(
+- 23, this->repeated_enum(i), target);
+- }
+-
+- // repeated .tests.Nested repeated_nested = 24;
+- for (int i = 0; i < this->repeated_nested_size(); i++) {
+- target = ::google::protobuf::internal::WireFormatLite::
+- WriteMessageNoVirtualToArray(
+- 24, this->repeated_nested(i), target);
+- }
+-
+- // repeated string empty = 25;
+- for (int i = 0; i < this->empty_size(); i++) {
+- ::google::protobuf::internal::WireFormat::VerifyUTF8String(
+- this->empty(i).data(), this->empty(i).length(),
+- ::google::protobuf::internal::WireFormat::SERIALIZE);
+- target = ::google::protobuf::internal::WireFormatLite::
+- WriteStringToArray(25, this->empty(i), target);
+- }
+-
+- if (!unknown_fields().empty()) {
+- target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray(
+- unknown_fields(), target);
+- }
+- return target;
+-}
+-
+-int Message::ByteSize() const {
+- int total_size = 0;
+-
+- if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+- // required string str = 1;
+- if (has_str()) {
+- total_size += 1 +
+- ::google::protobuf::internal::WireFormatLite::StringSize(
+- this->str());
+- }
+-
+- // required bytes bytes = 2;
+- if (has_bytes()) {
+- total_size += 1 +
+- ::google::protobuf::internal::WireFormatLite::BytesSize(
+- this->bytes());
+- }
+-
+- // optional int32 int32 = 3;
+- if (has_int32()) {
+- total_size += 1 +
+- ::google::protobuf::internal::WireFormatLite::Int32Size(
+- this->int32());
+- }
+-
+- // optional int64 int64 = 4;
+- if (has_int64()) {
+- total_size += 1 +
+- ::google::protobuf::internal::WireFormatLite::Int64Size(
+- this->int64());
+- }
+-
+- // optional uint32 uint32 = 5;
+- if (has_uint32()) {
+- total_size += 1 +
+- ::google::protobuf::internal::WireFormatLite::UInt32Size(
+- this->uint32());
+- }
+-
+- // optional uint64 uint64 = 6;
+- if (has_uint64()) {
+- total_size += 1 +
+- ::google::protobuf::internal::WireFormatLite::UInt64Size(
+- this->uint64());
+- }
+-
+- // optional sint32 sint32 = 7;
+- if (has_sint32()) {
+- total_size += 1 +
+- ::google::protobuf::internal::WireFormatLite::SInt32Size(
+- this->sint32());
+- }
+-
+- // optional sint64 sint64 = 8;
+- if (has_sint64()) {
+- total_size += 1 +
+- ::google::protobuf::internal::WireFormatLite::SInt64Size(
+- this->sint64());
+- }
+-
+- }
+- if (_has_bits_[8 / 32] & (0xffu << (8 % 32))) {
+- // required float f = 9;
+- if (has_f()) {
+- total_size += 1 + 4;
+- }
+-
+- // required double d = 10;
+- if (has_d()) {
+- total_size += 1 + 8;
+- }
+-
+- // required .tests.Enum e = 11;
+- if (has_e()) {
+- total_size += 1 +
+- ::google::protobuf::internal::WireFormatLite::EnumSize(this->e());
+- }
+-
+- // required .tests.Nested nested = 12;
+- if (has_nested()) {
+- total_size += 1 +
+- ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+- this->nested());
+- }
+-
+- }
+- // repeated string repeated_string = 13;
+- total_size += 1 * this->repeated_string_size();
+- for (int i = 0; i < this->repeated_string_size(); i++) {
+- total_size += ::google::protobuf::internal::WireFormatLite::StringSize(
+- this->repeated_string(i));
+- }
+-
+- // repeated bytes repeated_bytes = 14;
+- total_size += 1 * this->repeated_bytes_size();
+- for (int i = 0; i < this->repeated_bytes_size(); i++) {
+- total_size += ::google::protobuf::internal::WireFormatLite::BytesSize(
+- this->repeated_bytes(i));
+- }
+-
+- // repeated int32 repeated_int32 = 15;
+- {
+- int data_size = 0;
+- for (int i = 0; i < this->repeated_int32_size(); i++) {
+- data_size += ::google::protobuf::internal::WireFormatLite::
+- Int32Size(this->repeated_int32(i));
+- }
+- total_size += 1 * this->repeated_int32_size() + data_size;
+- }
+-
+- // repeated int64 repeated_int64 = 16;
+- {
+- int data_size = 0;
+- for (int i = 0; i < this->repeated_int64_size(); i++) {
+- data_size += ::google::protobuf::internal::WireFormatLite::
+- Int64Size(this->repeated_int64(i));
+- }
+- total_size += 2 * this->repeated_int64_size() + data_size;
+- }
+-
+- // repeated uint32 repeated_uint32 = 17;
+- {
+- int data_size = 0;
+- for (int i = 0; i < this->repeated_uint32_size(); i++) {
+- data_size += ::google::protobuf::internal::WireFormatLite::
+- UInt32Size(this->repeated_uint32(i));
+- }
+- total_size += 2 * this->repeated_uint32_size() + data_size;
+- }
+-
+- // repeated uint64 repeated_uint64 = 18;
+- {
+- int data_size = 0;
+- for (int i = 0; i < this->repeated_uint64_size(); i++) {
+- data_size += ::google::protobuf::internal::WireFormatLite::
+- UInt64Size(this->repeated_uint64(i));
+- }
+- total_size += 2 * this->repeated_uint64_size() + data_size;
+- }
+-
+- // repeated sint32 repeated_sint32 = 19;
+- {
+- int data_size = 0;
+- for (int i = 0; i < this->repeated_sint32_size(); i++) {
+- data_size += ::google::protobuf::internal::WireFormatLite::
+- SInt32Size(this->repeated_sint32(i));
+- }
+- total_size += 2 * this->repeated_sint32_size() + data_size;
+- }
+-
+- // repeated sint64 repeated_sint64 = 20;
+- {
+- int data_size = 0;
+- for (int i = 0; i < this->repeated_sint64_size(); i++) {
+- data_size += ::google::protobuf::internal::WireFormatLite::
+- SInt64Size(this->repeated_sint64(i));
+- }
+- total_size += 2 * this->repeated_sint64_size() + data_size;
+- }
+-
+- // repeated float repeated_float = 21;
+- {
+- int data_size = 0;
+- data_size = 4 * this->repeated_float_size();
+- total_size += 2 * this->repeated_float_size() + data_size;
+- }
+-
+- // repeated double repeated_double = 22;
+- {
+- int data_size = 0;
+- data_size = 8 * this->repeated_double_size();
+- total_size += 2 * this->repeated_double_size() + data_size;
+- }
+-
+- // repeated .tests.Enum repeated_enum = 23;
+- {
+- int data_size = 0;
+- for (int i = 0; i < this->repeated_enum_size(); i++) {
+- data_size += ::google::protobuf::internal::WireFormatLite::EnumSize(
+- this->repeated_enum(i));
+- }
+- total_size += 2 * this->repeated_enum_size() + data_size;
+- }
+-
+- // repeated .tests.Nested repeated_nested = 24;
+- total_size += 2 * this->repeated_nested_size();
+- for (int i = 0; i < this->repeated_nested_size(); i++) {
+- total_size +=
+- ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
+- this->repeated_nested(i));
+- }
+-
+- // repeated string empty = 25;
+- total_size += 2 * this->empty_size();
+- for (int i = 0; i < this->empty_size(); i++) {
+- total_size += ::google::protobuf::internal::WireFormatLite::StringSize(
+- this->empty(i));
+- }
+-
+- if (!unknown_fields().empty()) {
+- total_size +=
+- ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(
+- unknown_fields());
+- }
+- GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
+- _cached_size_ = total_size;
+- GOOGLE_SAFE_CONCURRENT_WRITES_END();
+- return total_size;
+-}
+-
+-void Message::MergeFrom(const ::google::protobuf::Message& from) {
+- GOOGLE_CHECK_NE(&from, this);
+- const Message* source =
+- ::google::protobuf::internal::dynamic_cast_if_available<const Message*>(
+- &from);
+- if (source == NULL) {
+- ::google::protobuf::internal::ReflectionOps::Merge(from, this);
+- } else {
+- MergeFrom(*source);
+- }
+-}
+-
+-void Message::MergeFrom(const Message& from) {
+- GOOGLE_CHECK_NE(&from, this);
+- repeated_string_.MergeFrom(from.repeated_string_);
+- repeated_bytes_.MergeFrom(from.repeated_bytes_);
+- repeated_int32_.MergeFrom(from.repeated_int32_);
+- repeated_int64_.MergeFrom(from.repeated_int64_);
+- repeated_uint32_.MergeFrom(from.repeated_uint32_);
+- repeated_uint64_.MergeFrom(from.repeated_uint64_);
+- repeated_sint32_.MergeFrom(from.repeated_sint32_);
+- repeated_sint64_.MergeFrom(from.repeated_sint64_);
+- repeated_float_.MergeFrom(from.repeated_float_);
+- repeated_double_.MergeFrom(from.repeated_double_);
+- repeated_enum_.MergeFrom(from.repeated_enum_);
+- repeated_nested_.MergeFrom(from.repeated_nested_);
+- empty_.MergeFrom(from.empty_);
+- if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
+- if (from.has_str()) {
+- set_str(from.str());
+- }
+- if (from.has_bytes()) {
+- set_bytes(from.bytes());
+- }
+- if (from.has_int32()) {
+- set_int32(from.int32());
+- }
+- if (from.has_int64()) {
+- set_int64(from.int64());
+- }
+- if (from.has_uint32()) {
+- set_uint32(from.uint32());
+- }
+- if (from.has_uint64()) {
+- set_uint64(from.uint64());
+- }
+- if (from.has_sint32()) {
+- set_sint32(from.sint32());
+- }
+- if (from.has_sint64()) {
+- set_sint64(from.sint64());
+- }
+- }
+- if (from._has_bits_[8 / 32] & (0xffu << (8 % 32))) {
+- if (from.has_f()) {
+- set_f(from.f());
+- }
+- if (from.has_d()) {
+- set_d(from.d());
+- }
+- if (from.has_e()) {
+- set_e(from.e());
+- }
+- if (from.has_nested()) {
+- mutable_nested()->::tests::Nested::MergeFrom(from.nested());
+- }
+- }
+- mutable_unknown_fields()->MergeFrom(from.unknown_fields());
+-}
+-
+-void Message::CopyFrom(const ::google::protobuf::Message& from) {
+- if (&from == this) return;
+- Clear();
+- MergeFrom(from);
+-}
+-
+-void Message::CopyFrom(const Message& from) {
+- if (&from == this) return;
+- Clear();
+- MergeFrom(from);
+-}
+-
+-bool Message::IsInitialized() const {
+- if ((_has_bits_[0] & 0x00000f03) != 0x00000f03) return false;
+-
+- return true;
+-}
+-
+-void Message::Swap(Message* other) {
+- if (other != this) {
+- std::swap(str_, other->str_);
+- std::swap(bytes_, other->bytes_);
+- std::swap(int32_, other->int32_);
+- std::swap(int64_, other->int64_);
+- std::swap(uint32_, other->uint32_);
+- std::swap(uint64_, other->uint64_);
+- std::swap(sint32_, other->sint32_);
+- std::swap(sint64_, other->sint64_);
+- std::swap(f_, other->f_);
+- std::swap(d_, other->d_);
+- std::swap(e_, other->e_);
+- std::swap(nested_, other->nested_);
+- repeated_string_.Swap(&other->repeated_string_);
+- repeated_bytes_.Swap(&other->repeated_bytes_);
+- repeated_int32_.Swap(&other->repeated_int32_);
+- repeated_int64_.Swap(&other->repeated_int64_);
+- repeated_uint32_.Swap(&other->repeated_uint32_);
+- repeated_uint64_.Swap(&other->repeated_uint64_);
+- repeated_sint32_.Swap(&other->repeated_sint32_);
+- repeated_sint64_.Swap(&other->repeated_sint64_);
+- repeated_float_.Swap(&other->repeated_float_);
+- repeated_double_.Swap(&other->repeated_double_);
+- repeated_enum_.Swap(&other->repeated_enum_);
+- repeated_nested_.Swap(&other->repeated_nested_);
+- empty_.Swap(&other->empty_);
+- std::swap(_has_bits_[0], other->_has_bits_[0]);
+- _unknown_fields_.Swap(&other->_unknown_fields_);
+- std::swap(_cached_size_, other->_cached_size_);
+- }
+-}
+-
+-::google::protobuf::Metadata Message::GetMetadata() const {
+- protobuf_AssignDescriptorsOnce();
+- ::google::protobuf::Metadata metadata;
+- metadata.descriptor = Message_descriptor_;
+- metadata.reflection = Message_reflection_;
+- return metadata;
+-}
+-
+-
+-// @@protoc_insertion_point(namespace_scope)
+-
+-} // namespace tests
+-
+-// @@protoc_insertion_point(global_scope)
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/protobuf_tests.pb.h b/3rdparty/libprocess/3rdparty/stout/tests/protobuf_tests.pb.h
+deleted file mode 100644
+index aef5b29..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/protobuf_tests.pb.h
++++ /dev/null
+@@ -1,1340 +0,0 @@
+-// Generated by the protocol buffer compiler. DO NOT EDIT!
+-// source: protobuf_tests.proto
+-
+-#ifndef PROTOBUF_protobuf_5ftests_2eproto__INCLUDED
+-#define PROTOBUF_protobuf_5ftests_2eproto__INCLUDED
+-
+-#include <string>
+-
+-#include <google/protobuf/stubs/common.h>
+-
+-#if GOOGLE_PROTOBUF_VERSION < 2004000
+-#error This file was generated by a newer version of protoc which is
+-#error incompatible with your Protocol Buffer headers. Please update
+-#error your headers.
+-#endif
+-#if 2004001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
+-#error This file was generated by an older version of protoc which is
+-#error incompatible with your Protocol Buffer headers. Please
+-#error regenerate this file with a newer version of protoc.
+-#endif
+-
+-#include <google/protobuf/generated_message_util.h>
+-#include <google/protobuf/repeated_field.h>
+-#include <google/protobuf/extension_set.h>
+-#include <google/protobuf/generated_message_reflection.h>
+-// @@protoc_insertion_point(includes)
+-
+-namespace tests {
+-
+-// Internal implementation detail -- do not call these.
+-void protobuf_AddDesc_protobuf_5ftests_2eproto();
+-void protobuf_AssignDesc_protobuf_5ftests_2eproto();
+-void protobuf_ShutdownFile_protobuf_5ftests_2eproto();
+-
+-class Nested;
+-class Message;
+-
+-enum Enum {
+- ONE = 1,
+- TWO = 2
+-};
+-bool Enum_IsValid(int value);
+-const Enum Enum_MIN = ONE;
+-const Enum Enum_MAX = TWO;
+-const int Enum_ARRAYSIZE = Enum_MAX + 1;
+-
+-const ::google::protobuf::EnumDescriptor* Enum_descriptor();
+-inline const ::std::string& Enum_Name(Enum value) {
+- return ::google::protobuf::internal::NameOfEnum(
+- Enum_descriptor(), value);
+-}
+-inline bool Enum_Parse(
+- const ::std::string& name, Enum* value) {
+- return ::google::protobuf::internal::ParseNamedEnum<Enum>(
+- Enum_descriptor(), name, value);
+-}
+-// ===================================================================
+-
+-class Nested : public ::google::protobuf::Message {
+- public:
+- Nested();
+- virtual ~Nested();
+-
+- Nested(const Nested& from);
+-
+- inline Nested& operator=(const Nested& from) {
+- CopyFrom(from);
+- return *this;
+- }
+-
+- inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {
+- return _unknown_fields_;
+- }
+-
+- inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {
+- return &_unknown_fields_;
+- }
+-
+- static const ::google::protobuf::Descriptor* descriptor();
+- static const Nested& default_instance();
+-
+- void Swap(Nested* other);
+-
+- // implements Message ----------------------------------------------
+-
+- Nested* New() const;
+- void CopyFrom(const ::google::protobuf::Message& from);
+- void MergeFrom(const ::google::protobuf::Message& from);
+- void CopyFrom(const Nested& from);
+- void MergeFrom(const Nested& from);
+- void Clear();
+- bool IsInitialized() const;
+-
+- int ByteSize() const;
+- bool MergePartialFromCodedStream(
+- ::google::protobuf::io::CodedInputStream* input);
+- void SerializeWithCachedSizes(
+- ::google::protobuf::io::CodedOutputStream* output) const;
+- ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;
+- int GetCachedSize() const { return _cached_size_; }
+- private:
+- void SharedCtor();
+- void SharedDtor();
+- void SetCachedSize(int size) const;
+- public:
+-
+- ::google::protobuf::Metadata GetMetadata() const;
+-
+- // nested types ----------------------------------------------------
+-
+- // accessors -------------------------------------------------------
+-
+- // optional string str = 1;
+- inline bool has_str() const;
+- inline void clear_str();
+- static const int kStrFieldNumber = 1;
+- inline const ::std::string& str() const;
+- inline void set_str(const ::std::string& value);
+- inline void set_str(const char* value);
+- inline void set_str(const char* value, size_t size);
+- inline ::std::string* mutable_str();
+- inline ::std::string* release_str();
+-
+- // @@protoc_insertion_point(class_scope:tests.Nested)
+- private:
+- inline void set_has_str();
+- inline void clear_has_str();
+-
+- ::google::protobuf::UnknownFieldSet _unknown_fields_;
+-
+- ::std::string* str_;
+-
+- mutable int _cached_size_;
+- ::google::protobuf::uint32 _has_bits_[(1 + 31) / 32];
+-
+- friend void protobuf_AddDesc_protobuf_5ftests_2eproto();
+- friend void protobuf_AssignDesc_protobuf_5ftests_2eproto();
+- friend void protobuf_ShutdownFile_protobuf_5ftests_2eproto();
+-
+- void InitAsDefaultInstance();
+- static Nested* default_instance_;
+-};
+-// -------------------------------------------------------------------
+-
+-class Message : public ::google::protobuf::Message {
+- public:
+- Message();
+- virtual ~Message();
+-
+- Message(const Message& from);
+-
+- inline Message& operator=(const Message& from) {
+- CopyFrom(from);
+- return *this;
+- }
+-
+- inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {
+- return _unknown_fields_;
+- }
+-
+- inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {
+- return &_unknown_fields_;
+- }
+-
+- static const ::google::protobuf::Descriptor* descriptor();
+- static const Message& default_instance();
+-
+- void Swap(Message* other);
+-
+- // implements Message ----------------------------------------------
+-
+- Message* New() const;
+- void CopyFrom(const ::google::protobuf::Message& from);
+- void MergeFrom(const ::google::protobuf::Message& from);
+- void CopyFrom(const Message& from);
+- void MergeFrom(const Message& from);
+- void Clear();
+- bool IsInitialized() const;
+-
+- int ByteSize() const;
+- bool MergePartialFromCodedStream(
+- ::google::protobuf::io::CodedInputStream* input);
+- void SerializeWithCachedSizes(
+- ::google::protobuf::io::CodedOutputStream* output) const;
+- ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;
+- int GetCachedSize() const { return _cached_size_; }
+- private:
+- void SharedCtor();
+- void SharedDtor();
+- void SetCachedSize(int size) const;
+- public:
+-
+- ::google::protobuf::Metadata GetMetadata() const;
+-
+- // nested types ----------------------------------------------------
+-
+- // accessors -------------------------------------------------------
+-
+- // required string str = 1;
+- inline bool has_str() const;
+- inline void clear_str();
+- static const int kStrFieldNumber = 1;
+- inline const ::std::string& str() const;
+- inline void set_str(const ::std::string& value);
+- inline void set_str(const char* value);
+- inline void set_str(const char* value, size_t size);
+- inline ::std::string* mutable_str();
+- inline ::std::string* release_str();
+-
+- // required bytes bytes = 2;
+- inline bool has_bytes() const;
+- inline void clear_bytes();
+- static const int kBytesFieldNumber = 2;
+- inline const ::std::string& bytes() const;
+- inline void set_bytes(const ::std::string& value);
+- inline void set_bytes(const char* value);
+- inline void set_bytes(const void* value, size_t size);
+- inline ::std::string* mutable_bytes();
+- inline ::std::string* release_bytes();
+-
+- // optional int32 int32 = 3;
+- inline bool has_int32() const;
+- inline void clear_int32();
+- static const int kInt32FieldNumber = 3;
+- inline ::google::protobuf::int32 int32() const;
+- inline void set_int32(::google::protobuf::int32 value);
+-
+- // optional int64 int64 = 4;
+- inline bool has_int64() const;
+- inline void clear_int64();
+- static const int kInt64FieldNumber = 4;
+- inline ::google::protobuf::int64 int64() const;
+- inline void set_int64(::google::protobuf::int64 value);
+-
+- // optional uint32 uint32 = 5;
+- inline bool has_uint32() const;
+- inline void clear_uint32();
+- static const int kUint32FieldNumber = 5;
+- inline ::google::protobuf::uint32 uint32() const;
+- inline void set_uint32(::google::protobuf::uint32 value);
+-
+- // optional uint64 uint64 = 6;
+- inline bool has_uint64() const;
+- inline void clear_uint64();
+- static const int kUint64FieldNumber = 6;
+- inline ::google::protobuf::uint64 uint64() const;
+- inline void set_uint64(::google::protobuf::uint64 value);
+-
+- // optional sint32 sint32 = 7;
+- inline bool has_sint32() const;
+- inline void clear_sint32();
+- static const int kSint32FieldNumber = 7;
+- inline ::google::protobuf::int32 sint32() const;
+- inline void set_sint32(::google::protobuf::int32 value);
+-
+- // optional sint64 sint64 = 8;
+- inline bool has_sint64() const;
+- inline void clear_sint64();
+- static const int kSint64FieldNumber = 8;
+- inline ::google::protobuf::int64 sint64() const;
+- inline void set_sint64(::google::protobuf::int64 value);
+-
+- // required float f = 9;
+- inline bool has_f() const;
+- inline void clear_f();
+- static const int kFFieldNumber = 9;
+- inline float f() const;
+- inline void set_f(float value);
+-
+- // required double d = 10;
+- inline bool has_d() const;
+- inline void clear_d();
+- static const int kDFieldNumber = 10;
+- inline double d() const;
+- inline void set_d(double value);
+-
+- // required .tests.Enum e = 11;
+- inline bool has_e() const;
+- inline void clear_e();
+- static const int kEFieldNumber = 11;
+- inline tests::Enum e() const;
+- inline void set_e(tests::Enum value);
+-
+- // required .tests.Nested nested = 12;
+- inline bool has_nested() const;
+- inline void clear_nested();
+- static const int kNestedFieldNumber = 12;
+- inline const ::tests::Nested& nested() const;
+- inline ::tests::Nested* mutable_nested();
+- inline ::tests::Nested* release_nested();
+-
+- // repeated string repeated_string = 13;
+- inline int repeated_string_size() const;
+- inline void clear_repeated_string();
+- static const int kRepeatedStringFieldNumber = 13;
+- inline const ::std::string& repeated_string(int index) const;
+- inline ::std::string* mutable_repeated_string(int index);
+- inline void set_repeated_string(int index, const ::std::string& value);
+- inline void set_repeated_string(int index, const char* value);
+- inline void set_repeated_string(int index, const char* value, size_t size);
+- inline ::std::string* add_repeated_string();
+- inline void add_repeated_string(const ::std::string& value);
+- inline void add_repeated_string(const char* value);
+- inline void add_repeated_string(const char* value, size_t size);
+- inline const ::google::protobuf::RepeatedPtrField< ::std::string>& repeated_string() const;
+- inline ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_repeated_string();
+-
+- // repeated bytes repeated_bytes = 14;
+- inline int repeated_bytes_size() const;
+- inline void clear_repeated_bytes();
+- static const int kRepeatedBytesFieldNumber = 14;
+- inline const ::std::string& repeated_bytes(int index) const;
+- inline ::std::string* mutable_repeated_bytes(int index);
+- inline void set_repeated_bytes(int index, const ::std::string& value);
+- inline void set_repeated_bytes(int index, const char* value);
+- inline void set_repeated_bytes(int index, const void* value, size_t size);
+- inline ::std::string* add_repeated_bytes();
+- inline void add_repeated_bytes(const ::std::string& value);
+- inline void add_repeated_bytes(const char* value);
+- inline void add_repeated_bytes(const void* value, size_t size);
+- inline const ::google::protobuf::RepeatedPtrField< ::std::string>& repeated_bytes() const;
+- inline ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_repeated_bytes();
+-
+- // repeated int32 repeated_int32 = 15;
+- inline int repeated_int32_size() const;
+- inline void clear_repeated_int32();
+- static const int kRepeatedInt32FieldNumber = 15;
+- inline ::google::protobuf::int32 repeated_int32(int index) const;
+- inline void set_repeated_int32(int index, ::google::protobuf::int32 value);
+- inline void add_repeated_int32(::google::protobuf::int32 value);
+- inline const ::google::protobuf::RepeatedField< ::google::protobuf::int32 >&
+- repeated_int32() const;
+- inline ::google::protobuf::RepeatedField< ::google::protobuf::int32 >*
+- mutable_repeated_int32();
+-
+- // repeated int64 repeated_int64 = 16;
+- inline int repeated_int64_size() const;
+- inline void clear_repeated_int64();
+- static const int kRepeatedInt64FieldNumber = 16;
+- inline ::google::protobuf::int64 repeated_int64(int index) const;
+- inline void set_repeated_int64(int index, ::google::protobuf::int64 value);
+- inline void add_repeated_int64(::google::protobuf::int64 value);
+- inline const ::google::protobuf::RepeatedField< ::google::protobuf::int64 >&
+- repeated_int64() const;
+- inline ::google::protobuf::RepeatedField< ::google::protobuf::int64 >*
+- mutable_repeated_int64();
+-
+- // repeated uint32 repeated_uint32 = 17;
+- inline int repeated_uint32_size() const;
+- inline void clear_repeated_uint32();
+- static const int kRepeatedUint32FieldNumber = 17;
+- inline ::google::protobuf::uint32 repeated_uint32(int index) const;
+- inline void set_repeated_uint32(int index, ::google::protobuf::uint32 value);
+- inline void add_repeated_uint32(::google::protobuf::uint32 value);
+- inline const ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >&
+- repeated_uint32() const;
+- inline ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >*
+- mutable_repeated_uint32();
+-
+- // repeated uint64 repeated_uint64 = 18;
+- inline int repeated_uint64_size() const;
+- inline void clear_repeated_uint64();
+- static const int kRepeatedUint64FieldNumber = 18;
+- inline ::google::protobuf::uint64 repeated_uint64(int index) const;
+- inline void set_repeated_uint64(int index, ::google::protobuf::uint64 value);
+- inline void add_repeated_uint64(::google::protobuf::uint64 value);
+- inline const ::google::protobuf::RepeatedField< ::google::protobuf::uint64 >&
+- repeated_uint64() const;
+- inline ::google::protobuf::RepeatedField< ::google::protobuf::uint64 >*
+- mutable_repeated_uint64();
+-
+- // repeated sint32 repeated_sint32 = 19;
+- inline int repeated_sint32_size() const;
+- inline void clear_repeated_sint32();
+- static const int kRepeatedSint32FieldNumber = 19;
+- inline ::google::protobuf::int32 repeated_sint32(int index) const;
+- inline void set_repeated_sint32(int index, ::google::protobuf::int32 value);
+- inline void add_repeated_sint32(::google::protobuf::int32 value);
+- inline const ::google::protobuf::RepeatedField< ::google::protobuf::int32 >&
+- repeated_sint32() const;
+- inline ::google::protobuf::RepeatedField< ::google::protobuf::int32 >*
+- mutable_repeated_sint32();
+-
+- // repeated sint64 repeated_sint64 = 20;
+- inline int repeated_sint64_size() const;
+- inline void clear_repeated_sint64();
+- static const int kRepeatedSint64FieldNumber = 20;
+- inline ::google::protobuf::int64 repeated_sint64(int index) const;
+- inline void set_repeated_sint64(int index, ::google::protobuf::int64 value);
+- inline void add_repeated_sint64(::google::protobuf::int64 value);
+- inline const ::google::protobuf::RepeatedField< ::google::protobuf::int64 >&
+- repeated_sint64() const;
+- inline ::google::protobuf::RepeatedField< ::google::protobuf::int64 >*
+- mutable_repeated_sint64();
+-
+- // repeated float repeated_float = 21;
+- inline int repeated_float_size() const;
+- inline void clear_repeated_float();
+- static const int kRepeatedFloatFieldNumber = 21;
+- inline float repeated_float(int index) const;
+- inline void set_repeated_float(int index, float value);
+- inline void add_repeated_float(float value);
+- inline const ::google::protobuf::RepeatedField< float >&
+- repeated_float() const;
+- inline ::google::protobuf::RepeatedField< float >*
+- mutable_repeated_float();
+-
+- // repeated double repeated_double = 22;
+- inline int repeated_double_size() const;
+- inline void clear_repeated_double();
+- static const int kRepeatedDoubleFieldNumber = 22;
+- inline double repeated_double(int index) const;
+- inline void set_repeated_double(int index, double value);
+- inline void add_repeated_double(double value);
+- inline const ::google::protobuf::RepeatedField< double >&
+- repeated_double() const;
+- inline ::google::protobuf::RepeatedField< double >*
+- mutable_repeated_double();
+-
+- // repeated .tests.Enum repeated_enum = 23;
+- inline int repeated_enum_size() const;
+- inline void clear_repeated_enum();
+- static const int kRepeatedEnumFieldNumber = 23;
+- inline tests::Enum repeated_enum(int index) const;
+- inline void set_repeated_enum(int index, tests::Enum value);
+- inline void add_repeated_enum(tests::Enum value);
+- inline const ::google::protobuf::RepeatedField<int>& repeated_enum() const;
+- inline ::google::protobuf::RepeatedField<int>* mutable_repeated_enum();
+-
+- // repeated .tests.Nested repeated_nested = 24;
+- inline int repeated_nested_size() const;
+- inline void clear_repeated_nested();
+- static const int kRepeatedNestedFieldNumber = 24;
+- inline const ::tests::Nested& repeated_nested(int index) const;
+- inline ::tests::Nested* mutable_repeated_nested(int index);
+- inline ::tests::Nested* add_repeated_nested();
+- inline const ::google::protobuf::RepeatedPtrField< ::tests::Nested >&
+- repeated_nested() const;
+- inline ::google::protobuf::RepeatedPtrField< ::tests::Nested >*
+- mutable_repeated_nested();
+-
+- // repeated string empty = 25;
+- inline int empty_size() const;
+- inline void clear_empty();
+- static const int kEmptyFieldNumber = 25;
+- inline const ::std::string& empty(int index) const;
+- inline ::std::string* mutable_empty(int index);
+- inline void set_empty(int index, const ::std::string& value);
+- inline void set_empty(int index, const char* value);
+- inline void set_empty(int index, const char* value, size_t size);
+- inline ::std::string* add_empty();
+- inline void add_empty(const ::std::string& value);
+- inline void add_empty(const char* value);
+- inline void add_empty(const char* value, size_t size);
+- inline const ::google::protobuf::RepeatedPtrField< ::std::string>& empty() const;
+- inline ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_empty();
+-
+- // @@protoc_insertion_point(class_scope:tests.Message)
+- private:
+- inline void set_has_str();
+- inline void clear_has_str();
+- inline void set_has_bytes();
+- inline void clear_has_bytes();
+- inline void set_has_int32();
+- inline void clear_has_int32();
+- inline void set_has_int64();
+- inline void clear_has_int64();
+- inline void set_has_uint32();
+- inline void clear_has_uint32();
+- inline void set_has_uint64();
+- inline void clear_has_uint64();
+- inline void set_has_sint32();
+- inline void clear_has_sint32();
+- inline void set_has_sint64();
+- inline void clear_has_sint64();
+- inline void set_has_f();
+- inline void clear_has_f();
+- inline void set_has_d();
+- inline void clear_has_d();
+- inline void set_has_e();
+- inline void clear_has_e();
+- inline void set_has_nested();
+- inline void clear_has_nested();
+-
+- ::google::protobuf::UnknownFieldSet _unknown_fields_;
+-
+- ::std::string* str_;
+- ::std::string* bytes_;
+- ::google::protobuf::int64 int64_;
+- ::google::protobuf::int32 int32_;
+- ::google::protobuf::uint32 uint32_;
+- ::google::protobuf::uint64 uint64_;
+- ::google::protobuf::int64 sint64_;
+- ::google::protobuf::int32 sint32_;
+- float f_;
+- double d_;
+- ::tests::Nested* nested_;
+- ::google::protobuf::RepeatedPtrField< ::std::string> repeated_string_;
+- ::google::protobuf::RepeatedPtrField< ::std::string> repeated_bytes_;
+- ::google::protobuf::RepeatedField< ::google::protobuf::int32 > repeated_int32_;
+- ::google::protobuf::RepeatedField< ::google::protobuf::int64 > repeated_int64_;
+- ::google::protobuf::RepeatedField< ::google::protobuf::uint32 > repeated_uint32_;
+- ::google::protobuf::RepeatedField< ::google::protobuf::uint64 > repeated_uint64_;
+- ::google::protobuf::RepeatedField< ::google::protobuf::int32 > repeated_sint32_;
+- ::google::protobuf::RepeatedField< ::google::protobuf::int64 > repeated_sint64_;
+- ::google::protobuf::RepeatedField< float > repeated_float_;
+- ::google::protobuf::RepeatedField< double > repeated_double_;
+- ::google::protobuf::RepeatedField<int> repeated_enum_;
+- ::google::protobuf::RepeatedPtrField< ::tests::Nested > repeated_nested_;
+- ::google::protobuf::RepeatedPtrField< ::std::string> empty_;
+- int e_;
+-
+- mutable int _cached_size_;
+- ::google::protobuf::uint32 _has_bits_[(25 + 31) / 32];
+-
+- friend void protobuf_AddDesc_protobuf_5ftests_2eproto();
+- friend void protobuf_AssignDesc_protobuf_5ftests_2eproto();
+- friend void protobuf_ShutdownFile_protobuf_5ftests_2eproto();
+-
+- void InitAsDefaultInstance();
+- static Message* default_instance_;
+-};
+-// ===================================================================
+-
+-
+-// ===================================================================
+-
+-// Nested
+-
+-// optional string str = 1;
+-inline bool Nested::has_str() const {
+- return (_has_bits_[0] & 0x00000001u) != 0;
+-}
+-inline void Nested::set_has_str() {
+- _has_bits_[0] |= 0x00000001u;
+-}
+-inline void Nested::clear_has_str() {
+- _has_bits_[0] &= ~0x00000001u;
+-}
+-inline void Nested::clear_str() {
+- if (str_ != &::google::protobuf::internal::kEmptyString) {
+- str_->clear();
+- }
+- clear_has_str();
+-}
+-inline const ::std::string& Nested::str() const {
+- return *str_;
+-}
+-inline void Nested::set_str(const ::std::string& value) {
+- set_has_str();
+- if (str_ == &::google::protobuf::internal::kEmptyString) {
+- str_ = new ::std::string;
+- }
+- str_->assign(value);
+-}
+-inline void Nested::set_str(const char* value) {
+- set_has_str();
+- if (str_ == &::google::protobuf::internal::kEmptyString) {
+- str_ = new ::std::string;
+- }
+- str_->assign(value);
+-}
+-inline void Nested::set_str(const char* value, size_t size) {
+- set_has_str();
+- if (str_ == &::google::protobuf::internal::kEmptyString) {
+- str_ = new ::std::string;
+- }
+- str_->assign(reinterpret_cast<const char*>(value), size);
+-}
+-inline ::std::string* Nested::mutable_str() {
+- set_has_str();
+- if (str_ == &::google::protobuf::internal::kEmptyString) {
+- str_ = new ::std::string;
+- }
+- return str_;
+-}
+-inline ::std::string* Nested::release_str() {
+- clear_has_str();
+- if (str_ == &::google::protobuf::internal::kEmptyString) {
+- return NULL;
+- } else {
+- ::std::string* temp = str_;
+- str_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString);
+- return temp;
+- }
+-}
+-
+-// -------------------------------------------------------------------
+-
+-// Message
+-
+-// required string str = 1;
+-inline bool Message::has_str() const {
+- return (_has_bits_[0] & 0x00000001u) != 0;
+-}
+-inline void Message::set_has_str() {
+- _has_bits_[0] |= 0x00000001u;
+-}
+-inline void Message::clear_has_str() {
+- _has_bits_[0] &= ~0x00000001u;
+-}
+-inline void Message::clear_str() {
+- if (str_ != &::google::protobuf::internal::kEmptyString) {
+- str_->clear();
+- }
+- clear_has_str();
+-}
+-inline const ::std::string& Message::str() const {
+- return *str_;
+-}
+-inline void Message::set_str(const ::std::string& value) {
+- set_has_str();
+- if (str_ == &::google::protobuf::internal::kEmptyString) {
+- str_ = new ::std::string;
+- }
+- str_->assign(value);
+-}
+-inline void Message::set_str(const char* value) {
+- set_has_str();
+- if (str_ == &::google::protobuf::internal::kEmptyString) {
+- str_ = new ::std::string;
+- }
+- str_->assign(value);
+-}
+-inline void Message::set_str(const char* value, size_t size) {
+- set_has_str();
+- if (str_ == &::google::protobuf::internal::kEmptyString) {
+- str_ = new ::std::string;
+- }
+- str_->assign(reinterpret_cast<const char*>(value), size);
+-}
+-inline ::std::string* Message::mutable_str() {
+- set_has_str();
+- if (str_ == &::google::protobuf::internal::kEmptyString) {
+- str_ = new ::std::string;
+- }
+- return str_;
+-}
+-inline ::std::string* Message::release_str() {
+- clear_has_str();
+- if (str_ == &::google::protobuf::internal::kEmptyString) {
+- return NULL;
+- } else {
+- ::std::string* temp = str_;
+- str_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString);
+- return temp;
+- }
+-}
+-
+-// required bytes bytes = 2;
+-inline bool Message::has_bytes() const {
+- return (_has_bits_[0] & 0x00000002u) != 0;
+-}
+-inline void Message::set_has_bytes() {
+- _has_bits_[0] |= 0x00000002u;
+-}
+-inline void Message::clear_has_bytes() {
+- _has_bits_[0] &= ~0x00000002u;
+-}
+-inline void Message::clear_bytes() {
+- if (bytes_ != &::google::protobuf::internal::kEmptyString) {
+- bytes_->clear();
+- }
+- clear_has_bytes();
+-}
+-inline const ::std::string& Message::bytes() const {
+- return *bytes_;
+-}
+-inline void Message::set_bytes(const ::std::string& value) {
+- set_has_bytes();
+- if (bytes_ == &::google::protobuf::internal::kEmptyString) {
+- bytes_ = new ::std::string;
+- }
+- bytes_->assign(value);
+-}
+-inline void Message::set_bytes(const char* value) {
+- set_has_bytes();
+- if (bytes_ == &::google::protobuf::internal::kEmptyString) {
+- bytes_ = new ::std::string;
+- }
+- bytes_->assign(value);
+-}
+-inline void Message::set_bytes(const void* value, size_t size) {
+- set_has_bytes();
+- if (bytes_ == &::google::protobuf::internal::kEmptyString) {
+- bytes_ = new ::std::string;
+- }
+- bytes_->assign(reinterpret_cast<const char*>(value), size);
+-}
+-inline ::std::string* Message::mutable_bytes() {
+- set_has_bytes();
+- if (bytes_ == &::google::protobuf::internal::kEmptyString) {
+- bytes_ = new ::std::string;
+- }
+- return bytes_;
+-}
+-inline ::std::string* Message::release_bytes() {
+- clear_has_bytes();
+- if (bytes_ == &::google::protobuf::internal::kEmptyString) {
+- return NULL;
+- } else {
+- ::std::string* temp = bytes_;
+- bytes_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString);
+- return temp;
+- }
+-}
+-
+-// optional int32 int32 = 3;
+-inline bool Message::has_int32() const {
+- return (_has_bits_[0] & 0x00000004u) != 0;
+-}
+-inline void Message::set_has_int32() {
+- _has_bits_[0] |= 0x00000004u;
+-}
+-inline void Message::clear_has_int32() {
+- _has_bits_[0] &= ~0x00000004u;
+-}
+-inline void Message::clear_int32() {
+- int32_ = 0;
+- clear_has_int32();
+-}
+-inline ::google::protobuf::int32 Message::int32() const {
+- return int32_;
+-}
+-inline void Message::set_int32(::google::protobuf::int32 value) {
+- set_has_int32();
+- int32_ = value;
+-}
+-
+-// optional int64 int64 = 4;
+-inline bool Message::has_int64() const {
+- return (_has_bits_[0] & 0x00000008u) != 0;
+-}
+-inline void Message::set_has_int64() {
+- _has_bits_[0] |= 0x00000008u;
+-}
+-inline void Message::clear_has_int64() {
+- _has_bits_[0] &= ~0x00000008u;
+-}
+-inline void Message::clear_int64() {
+- int64_ = GOOGLE_LONGLONG(0);
+- clear_has_int64();
+-}
+-inline ::google::protobuf::int64 Message::int64() const {
+- return int64_;
+-}
+-inline void Message::set_int64(::google::protobuf::int64 value) {
+- set_has_int64();
+- int64_ = value;
+-}
+-
+-// optional uint32 uint32 = 5;
+-inline bool Message::has_uint32() const {
+- return (_has_bits_[0] & 0x00000010u) != 0;
+-}
+-inline void Message::set_has_uint32() {
+- _has_bits_[0] |= 0x00000010u;
+-}
+-inline void Message::clear_has_uint32() {
+- _has_bits_[0] &= ~0x00000010u;
+-}
+-inline void Message::clear_uint32() {
+- uint32_ = 0u;
+- clear_has_uint32();
+-}
+-inline ::google::protobuf::uint32 Message::uint32() const {
+- return uint32_;
+-}
+-inline void Message::set_uint32(::google::protobuf::uint32 value) {
+- set_has_uint32();
+- uint32_ = value;
+-}
+-
+-// optional uint64 uint64 = 6;
+-inline bool Message::has_uint64() const {
+- return (_has_bits_[0] & 0x00000020u) != 0;
+-}
+-inline void Message::set_has_uint64() {
+- _has_bits_[0] |= 0x00000020u;
+-}
+-inline void Message::clear_has_uint64() {
+- _has_bits_[0] &= ~0x00000020u;
+-}
+-inline void Message::clear_uint64() {
+- uint64_ = GOOGLE_ULONGLONG(0);
+- clear_has_uint64();
+-}
+-inline ::google::protobuf::uint64 Message::uint64() const {
+- return uint64_;
+-}
+-inline void Message::set_uint64(::google::protobuf::uint64 value) {
+- set_has_uint64();
+- uint64_ = value;
+-}
+-
+-// optional sint32 sint32 = 7;
+-inline bool Message::has_sint32() const {
+- return (_has_bits_[0] & 0x00000040u) != 0;
+-}
+-inline void Message::set_has_sint32() {
+- _has_bits_[0] |= 0x00000040u;
+-}
+-inline void Message::clear_has_sint32() {
+- _has_bits_[0] &= ~0x00000040u;
+-}
+-inline void Message::clear_sint32() {
+- sint32_ = 0;
+- clear_has_sint32();
+-}
+-inline ::google::protobuf::int32 Message::sint32() const {
+- return sint32_;
+-}
+-inline void Message::set_sint32(::google::protobuf::int32 value) {
+- set_has_sint32();
+- sint32_ = value;
+-}
+-
+-// optional sint64 sint64 = 8;
+-inline bool Message::has_sint64() const {
+- return (_has_bits_[0] & 0x00000080u) != 0;
+-}
+-inline void Message::set_has_sint64() {
+- _has_bits_[0] |= 0x00000080u;
+-}
+-inline void Message::clear_has_sint64() {
+- _has_bits_[0] &= ~0x00000080u;
+-}
+-inline void Message::clear_sint64() {
+- sint64_ = GOOGLE_LONGLONG(0);
+- clear_has_sint64();
+-}
+-inline ::google::protobuf::int64 Message::sint64() const {
+- return sint64_;
+-}
+-inline void Message::set_sint64(::google::protobuf::int64 value) {
+- set_has_sint64();
+- sint64_ = value;
+-}
+-
+-// required float f = 9;
+-inline bool Message::has_f() const {
+- return (_has_bits_[0] & 0x00000100u) != 0;
+-}
+-inline void Message::set_has_f() {
+- _has_bits_[0] |= 0x00000100u;
+-}
+-inline void Message::clear_has_f() {
+- _has_bits_[0] &= ~0x00000100u;
+-}
+-inline void Message::clear_f() {
+- f_ = 0;
+- clear_has_f();
+-}
+-inline float Message::f() const {
+- return f_;
+-}
+-inline void Message::set_f(float value) {
+- set_has_f();
+- f_ = value;
+-}
+-
+-// required double d = 10;
+-inline bool Message::has_d() const {
+- return (_has_bits_[0] & 0x00000200u) != 0;
+-}
+-inline void Message::set_has_d() {
+- _has_bits_[0] |= 0x00000200u;
+-}
+-inline void Message::clear_has_d() {
+- _has_bits_[0] &= ~0x00000200u;
+-}
+-inline void Message::clear_d() {
+- d_ = 0;
+- clear_has_d();
+-}
+-inline double Message::d() const {
+- return d_;
+-}
+-inline void Message::set_d(double value) {
+- set_has_d();
+- d_ = value;
+-}
+-
+-// required .tests.Enum e = 11;
+-inline bool Message::has_e() const {
+- return (_has_bits_[0] & 0x00000400u) != 0;
+-}
+-inline void Message::set_has_e() {
+- _has_bits_[0] |= 0x00000400u;
+-}
+-inline void Message::clear_has_e() {
+- _has_bits_[0] &= ~0x00000400u;
+-}
+-inline void Message::clear_e() {
+- e_ = 1;
+- clear_has_e();
+-}
+-inline tests::Enum Message::e() const {
+- return static_cast< tests::Enum >(e_);
+-}
+-inline void Message::set_e(tests::Enum value) {
+- GOOGLE_DCHECK(tests::Enum_IsValid(value));
+- set_has_e();
+- e_ = value;
+-}
+-
+-// required .tests.Nested nested = 12;
+-inline bool Message::has_nested() const {
+- return (_has_bits_[0] & 0x00000800u) != 0;
+-}
+-inline void Message::set_has_nested() {
+- _has_bits_[0] |= 0x00000800u;
+-}
+-inline void Message::clear_has_nested() {
+- _has_bits_[0] &= ~0x00000800u;
+-}
+-inline void Message::clear_nested() {
+- if (nested_ != NULL) nested_->::tests::Nested::Clear();
+- clear_has_nested();
+-}
+-inline const ::tests::Nested& Message::nested() const {
+- return nested_ != NULL ? *nested_ : *default_instance_->nested_;
+-}
+-inline ::tests::Nested* Message::mutable_nested() {
+- set_has_nested();
+- if (nested_ == NULL) nested_ = new ::tests::Nested;
+- return nested_;
+-}
+-inline ::tests::Nested* Message::release_nested() {
+- clear_has_nested();
+- ::tests::Nested* temp = nested_;
+- nested_ = NULL;
+- return temp;
+-}
+-
+-// repeated string repeated_string = 13;
+-inline int Message::repeated_string_size() const {
+- return repeated_string_.size();
+-}
+-inline void Message::clear_repeated_string() {
+- repeated_string_.Clear();
+-}
+-inline const ::std::string& Message::repeated_string(int index) const {
+- return repeated_string_.Get(index);
+-}
+-inline ::std::string* Message::mutable_repeated_string(int index) {
+- return repeated_string_.Mutable(index);
+-}
+-inline void Message::set_repeated_string(int index, const ::std::string& value) {
+- repeated_string_.Mutable(index)->assign(value);
+-}
+-inline void Message::set_repeated_string(int index, const char* value) {
+- repeated_string_.Mutable(index)->assign(value);
+-}
+-inline void Message::set_repeated_string(int index, const char* value, size_t size) {
+- repeated_string_.Mutable(index)->assign(
+- reinterpret_cast<const char*>(value), size);
+-}
+-inline ::std::string* Message::add_repeated_string() {
+- return repeated_string_.Add();
+-}
+-inline void Message::add_repeated_string(const ::std::string& value) {
+- repeated_string_.Add()->assign(value);
+-}
+-inline void Message::add_repeated_string(const char* value) {
+- repeated_string_.Add()->assign(value);
+-}
+-inline void Message::add_repeated_string(const char* value, size_t size) {
+- repeated_string_.Add()->assign(reinterpret_cast<const char*>(value), size);
+-}
+-inline const ::google::protobuf::RepeatedPtrField< ::std::string>&
+-Message::repeated_string() const {
+- return repeated_string_;
+-}
+-inline ::google::protobuf::RepeatedPtrField< ::std::string>*
+-Message::mutable_repeated_string() {
+- return &repeated_string_;
+-}
+-
+-// repeated bytes repeated_bytes = 14;
+-inline int Message::repeated_bytes_size() const {
+- return repeated_bytes_.size();
+-}
+-inline void Message::clear_repeated_bytes() {
+- repeated_bytes_.Clear();
+-}
+-inline const ::std::string& Message::repeated_bytes(int index) const {
+- return repeated_bytes_.Get(index);
+-}
+-inline ::std::string* Message::mutable_repeated_bytes(int index) {
+- return repeated_bytes_.Mutable(index);
+-}
+-inline void Message::set_repeated_bytes(int index, const ::std::string& value) {
+- repeated_bytes_.Mutable(index)->assign(value);
+-}
+-inline void Message::set_repeated_bytes(int index, const char* value) {
+- repeated_bytes_.Mutable(index)->assign(value);
+-}
+-inline void Message::set_repeated_bytes(int index, const void* value, size_t size) {
+- repeated_bytes_.Mutable(index)->assign(
+- reinterpret_cast<const char*>(value), size);
+-}
+-inline ::std::string* Message::add_repeated_bytes() {
+- return repeated_bytes_.Add();
+-}
+-inline void Message::add_repeated_bytes(const ::std::string& value) {
+- repeated_bytes_.Add()->assign(value);
+-}
+-inline void Message::add_repeated_bytes(const char* value) {
+- repeated_bytes_.Add()->assign(value);
+-}
+-inline void Message::add_repeated_bytes(const void* value, size_t size) {
+- repeated_bytes_.Add()->assign(reinterpret_cast<const char*>(value), size);
+-}
+-inline const ::google::protobuf::RepeatedPtrField< ::std::string>&
+-Message::repeated_bytes() const {
+- return repeated_bytes_;
+-}
+-inline ::google::protobuf::RepeatedPtrField< ::std::string>*
+-Message::mutable_repeated_bytes() {
+- return &repeated_bytes_;
+-}
+-
+-// repeated int32 repeated_int32 = 15;
+-inline int Message::repeated_int32_size() const {
+- return repeated_int32_.size();
+-}
+-inline void Message::clear_repeated_int32() {
+- repeated_int32_.Clear();
+-}
+-inline ::google::protobuf::int32 Message::repeated_int32(int index) const {
+- return repeated_int32_.Get(index);
+-}
+-inline void Message::set_repeated_int32(int index, ::google::protobuf::int32 value) {
+- repeated_int32_.Set(index, value);
+-}
+-inline void Message::add_repeated_int32(::google::protobuf::int32 value) {
+- repeated_int32_.Add(value);
+-}
+-inline const ::google::protobuf::RepeatedField< ::google::protobuf::int32 >&
+-Message::repeated_int32() const {
+- return repeated_int32_;
+-}
+-inline ::google::protobuf::RepeatedField< ::google::protobuf::int32 >*
+-Message::mutable_repeated_int32() {
+- return &repeated_int32_;
+-}
+-
+-// repeated int64 repeated_int64 = 16;
+-inline int Message::repeated_int64_size() const {
+- return repeated_int64_.size();
+-}
+-inline void Message::clear_repeated_int64() {
+- repeated_int64_.Clear();
+-}
+-inline ::google::protobuf::int64 Message::repeated_int64(int index) const {
+- return repeated_int64_.Get(index);
+-}
+-inline void Message::set_repeated_int64(int index, ::google::protobuf::int64 value) {
+- repeated_int64_.Set(index, value);
+-}
+-inline void Message::add_repeated_int64(::google::protobuf::int64 value) {
+- repeated_int64_.Add(value);
+-}
+-inline const ::google::protobuf::RepeatedField< ::google::protobuf::int64 >&
+-Message::repeated_int64() const {
+- return repeated_int64_;
+-}
+-inline ::google::protobuf::RepeatedField< ::google::protobuf::int64 >*
+-Message::mutable_repeated_int64() {
+- return &repeated_int64_;
+-}
+-
+-// repeated uint32 repeated_uint32 = 17;
+-inline int Message::repeated_uint32_size() const {
+- return repeated_uint32_.size();
+-}
+-inline void Message::clear_repeated_uint32() {
+- repeated_uint32_.Clear();
+-}
+-inline ::google::protobuf::uint32 Message::repeated_uint32(int index) const {
+- return repeated_uint32_.Get(index);
+-}
+-inline void Message::set_repeated_uint32(int index, ::google::protobuf::uint32 value) {
+- repeated_uint32_.Set(index, value);
+-}
+-inline void Message::add_repeated_uint32(::google::protobuf::uint32 value) {
+- repeated_uint32_.Add(value);
+-}
+-inline const ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >&
+-Message::repeated_uint32() const {
+- return repeated_uint32_;
+-}
+-inline ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >*
+-Message::mutable_repeated_uint32() {
+- return &repeated_uint32_;
+-}
+-
+-// repeated uint64 repeated_uint64 = 18;
+-inline int Message::repeated_uint64_size() const {
+- return repeated_uint64_.size();
+-}
+-inline void Message::clear_repeated_uint64() {
+- repeated_uint64_.Clear();
+-}
+-inline ::google::protobuf::uint64 Message::repeated_uint64(int index) const {
+- return repeated_uint64_.Get(index);
+-}
+-inline void Message::set_repeated_uint64(int index, ::google::protobuf::uint64 value) {
+- repeated_uint64_.Set(index, value);
+-}
+-inline void Message::add_repeated_uint64(::google::protobuf::uint64 value) {
+- repeated_uint64_.Add(value);
+-}
+-inline const ::google::protobuf::RepeatedField< ::google::protobuf::uint64 >&
+-Message::repeated_uint64() const {
+- return repeated_uint64_;
+-}
+-inline ::google::protobuf::RepeatedField< ::google::protobuf::uint64 >*
+-Message::mutable_repeated_uint64() {
+- return &repeated_uint64_;
+-}
+-
+-// repeated sint32 repeated_sint32 = 19;
+-inline int Message::repeated_sint32_size() const {
+- return repeated_sint32_.size();
+-}
+-inline void Message::clear_repeated_sint32() {
+- repeated_sint32_.Clear();
+-}
+-inline ::google::protobuf::int32 Message::repeated_sint32(int index) const {
+- return repeated_sint32_.Get(index);
+-}
+-inline void Message::set_repeated_sint32(int index, ::google::protobuf::int32 value) {
+- repeated_sint32_.Set(index, value);
+-}
+-inline void Message::add_repeated_sint32(::google::protobuf::int32 value) {
+- repeated_sint32_.Add(value);
+-}
+-inline const ::google::protobuf::RepeatedField< ::google::protobuf::int32 >&
+-Message::repeated_sint32() const {
+- return repeated_sint32_;
+-}
+-inline ::google::protobuf::RepeatedField< ::google::protobuf::int32 >*
+-Message::mutable_repeated_sint32() {
+- return &repeated_sint32_;
+-}
+-
+-// repeated sint64 repeated_sint64 = 20;
+-inline int Message::repeated_sint64_size() const {
+- return repeated_sint64_.size();
+-}
+-inline void Message::clear_repeated_sint64() {
+- repeated_sint64_.Clear();
+-}
+-inline ::google::protobuf::int64 Message::repeated_sint64(int index) const {
+- return repeated_sint64_.Get(index);
+-}
+-inline void Message::set_repeated_sint64(int index, ::google::protobuf::int64 value) {
+- repeated_sint64_.Set(index, value);
+-}
+-inline void Message::add_repeated_sint64(::google::protobuf::int64 value) {
+- repeated_sint64_.Add(value);
+-}
+-inline const ::google::protobuf::RepeatedField< ::google::protobuf::int64 >&
+-Message::repeated_sint64() const {
+- return repeated_sint64_;
+-}
+-inline ::google::protobuf::RepeatedField< ::google::protobuf::int64 >*
+-Message::mutable_repeated_sint64() {
+- return &repeated_sint64_;
+-}
+-
+-// repeated float repeated_float = 21;
+-inline int Message::repeated_float_size() const {
+- return repeated_float_.size();
+-}
+-inline void Message::clear_repeated_float() {
+- repeated_float_.Clear();
+-}
+-inline float Message::repeated_float(int index) const {
+- return repeated_float_.Get(index);
+-}
+-inline void Message::set_repeated_float(int index, float value) {
+- repeated_float_.Set(index, value);
+-}
+-inline void Message::add_repeated_float(float value) {
+- repeated_float_.Add(value);
+-}
+-inline const ::google::protobuf::RepeatedField< float >&
+-Message::repeated_float() const {
+- return repeated_float_;
+-}
+-inline ::google::protobuf::RepeatedField< float >*
+-Message::mutable_repeated_float() {
+- return &repeated_float_;
+-}
+-
+-// repeated double repeated_double = 22;
+-inline int Message::repeated_double_size() const {
+- return repeated_double_.size();
+-}
+-inline void Message::clear_repeated_double() {
+- repeated_double_.Clear();
+-}
+-inline double Message::repeated_double(int index) const {
+- return repeated_double_.Get(index);
+-}
+-inline void Message::set_repeated_double(int index, double value) {
+- repeated_double_.Set(index, value);
+-}
+-inline void Message::add_repeated_double(double value) {
+- repeated_double_.Add(value);
+-}
+-inline const ::google::protobuf::RepeatedField< double >&
+-Message::repeated_double() const {
+- return repeated_double_;
+-}
+-inline ::google::protobuf::RepeatedField< double >*
+-Message::mutable_repeated_double() {
+- return &repeated_double_;
+-}
+-
+-// repeated .tests.Enum repeated_enum = 23;
+-inline int Message::repeated_enum_size() const {
+- return repeated_enum_.size();
+-}
+-inline void Message::clear_repeated_enum() {
+- repeated_enum_.Clear();
+-}
+-inline tests::Enum Message::repeated_enum(int index) const {
+- return static_cast< tests::Enum >(repeated_enum_.Get(index));
+-}
+-inline void Message::set_repeated_enum(int index, tests::Enum value) {
+- GOOGLE_DCHECK(tests::Enum_IsValid(value));
+- repeated_enum_.Set(index, value);
+-}
+-inline void Message::add_repeated_enum(tests::Enum value) {
+- GOOGLE_DCHECK(tests::Enum_IsValid(value));
+- repeated_enum_.Add(value);
+-}
+-inline const ::google::protobuf::RepeatedField<int>&
+-Message::repeated_enum() const {
+- return repeated_enum_;
+-}
+-inline ::google::protobuf::RepeatedField<int>*
+-Message::mutable_repeated_enum() {
+- return &repeated_enum_;
+-}
+-
+-// repeated .tests.Nested repeated_nested = 24;
+-inline int Message::repeated_nested_size() const {
+- return repeated_nested_.size();
+-}
+-inline void Message::clear_repeated_nested() {
+- repeated_nested_.Clear();
+-}
+-inline const ::tests::Nested& Message::repeated_nested(int index) const {
+- return repeated_nested_.Get(index);
+-}
+-inline ::tests::Nested* Message::mutable_repeated_nested(int index) {
+- return repeated_nested_.Mutable(index);
+-}
+-inline ::tests::Nested* Message::add_repeated_nested() {
+- return repeated_nested_.Add();
+-}
+-inline const ::google::protobuf::RepeatedPtrField< ::tests::Nested >&
+-Message::repeated_nested() const {
+- return repeated_nested_;
+-}
+-inline ::google::protobuf::RepeatedPtrField< ::tests::Nested >*
+-Message::mutable_repeated_nested() {
+- return &repeated_nested_;
+-}
+-
+-// repeated string empty = 25;
+-inline int Message::empty_size() const {
+- return empty_.size();
+-}
+-inline void Message::clear_empty() {
+- empty_.Clear();
+-}
+-inline const ::std::string& Message::empty(int index) const {
+- return empty_.Get(index);
+-}
+-inline ::std::string* Message::mutable_empty(int index) {
+- return empty_.Mutable(index);
+-}
+-inline void Message::set_empty(int index, const ::std::string& value) {
+- empty_.Mutable(index)->assign(value);
+-}
+-inline void Message::set_empty(int index, const char* value) {
+- empty_.Mutable(index)->assign(value);
+-}
+-inline void Message::set_empty(int index, const char* value, size_t size) {
+- empty_.Mutable(index)->assign(
+- reinterpret_cast<const char*>(value), size);
+-}
+-inline ::std::string* Message::add_empty() {
+- return empty_.Add();
+-}
+-inline void Message::add_empty(const ::std::string& value) {
+- empty_.Add()->assign(value);
+-}
+-inline void Message::add_empty(const char* value) {
+- empty_.Add()->assign(value);
+-}
+-inline void Message::add_empty(const char* value, size_t size) {
+- empty_.Add()->assign(reinterpret_cast<const char*>(value), size);
+-}
+-inline const ::google::protobuf::RepeatedPtrField< ::std::string>&
+-Message::empty() const {
+- return empty_;
+-}
+-inline ::google::protobuf::RepeatedPtrField< ::std::string>*
+-Message::mutable_empty() {
+- return &empty_;
+-}
+-
+-
+-// @@protoc_insertion_point(namespace_scope)
+-
+-} // namespace tests
+-
+-#ifndef SWIG
+-namespace google {
+-namespace protobuf {
+-
+-template <>
+-inline const EnumDescriptor* GetEnumDescriptor< tests::Enum>() {
+- return tests::Enum_descriptor();
+-}
+-
+-} // namespace google
+-} // namespace protobuf
+-#endif // SWIG
+-
+-// @@protoc_insertion_point(global_scope)
+-
+-#endif // PROTOBUF_protobuf_5ftests_2eproto__INCLUDED
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/protobuf_tests.proto b/3rdparty/libprocess/3rdparty/stout/tests/protobuf_tests.proto
+deleted file mode 100644
+index 146cc20..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/protobuf_tests.proto
++++ /dev/null
+@@ -1,63 +0,0 @@
+-package tests;
+-
+-// NOTE: The generated headers for this file have been included
+-// in the tests folder to simplify the build process (no need to
+-// have protoc available to compile this file). As a result, if
+-// there are any changes to this file, the headers must be
+-// re-generated and committed alongside changes to this file.
+-// There is a TODO in protobuf_tests.cpp that demonstrates how
+-// to avoid the need for this file entirely by generating a
+-// dynamic message at run-time.
+-
+-enum Enum {
+- ONE = 1;
+- TWO = 2;
+-}
+-
+-
+-message Nested {
+- optional string str = 1;
+-}
+-
+-
+-// An elaborate message for testing Proto->JSON conversion.
+-message Message {
+- required string str = 1;
+-
+- required bytes bytes = 2;
+-
+- optional int32 int32 = 3;
+- optional int64 int64 = 4;
+- optional uint32 uint32 = 5;
+- optional uint64 uint64 = 6;
+- optional sint32 sint32 = 7;
+- optional sint64 sint64 = 8;
+-
+- required float f = 9;
+- required double d = 10;
+-
+- required Enum e = 11;
+-
+- required Nested nested = 12;
+-
+- repeated string repeated_string = 13;
+-
+- repeated bytes repeated_bytes = 14;
+-
+- repeated int32 repeated_int32 = 15;
+- repeated int64 repeated_int64 = 16;
+- repeated uint32 repeated_uint32 = 17;
+- repeated uint64 repeated_uint64 = 18;
+- repeated sint32 repeated_sint32 = 19;
+- repeated sint64 repeated_sint64 = 20;
+-
+- repeated float repeated_float = 21;
+- repeated double repeated_double = 22;
+-
+- repeated Enum repeated_enum = 23;
+-
+- repeated Nested repeated_nested = 24;
+-
+- repeated string empty = 25;
+-}
+-
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/set_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/set_tests.cpp
+deleted file mode 100644
+index cdedacd..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/set_tests.cpp
++++ /dev/null
+@@ -1,28 +0,0 @@
+-#include <gtest/gtest.h>
+-
+-#include <stout/set.hpp>
+-
+-TEST(Stout, Set)
+-{
+- EXPECT_EQ(Set<int>(1, 2), Set<int>(1) | Set<int>(2));
+- EXPECT_EQ(Set<int>(1, 2), Set<int>(1, 2) | Set<int>(1));
+- EXPECT_EQ(Set<int>(1, 2), Set<int>(1) + 2);
+- EXPECT_EQ(Set<int>(1, 2), Set<int>(1, 2) + 2);
+- EXPECT_EQ(Set<int>(1, 2), Set<int>(1, 2, 3) & Set<int>(1, 2));
+- EXPECT_EQ(Set<int>(1, 2), Set<int>(1, 2) & Set<int>(1, 2));
+-
+- Set<int> left;
+- left.insert(2);
+- left.insert(4);
+-
+- Set<int> right;
+- right.insert(1);
+- right.insert(3);
+-
+- EXPECT_EQ(Set<int>(1, 2, 3, 4), left | right);
+- EXPECT_EQ(Set<int>(), left & right);
+-
+- std::set<int> s = left;
+-
+- EXPECT_EQ(Set<int>(2, 4, 6), s + 6);
+-}
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/some_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/some_tests.cpp
+deleted file mode 100644
+index 4041dc4..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/some_tests.cpp
++++ /dev/null
+@@ -1,67 +0,0 @@
+-#include <gtest/gtest.h>
+-
+-#include <map>
+-#include <string>
+-
+-#include <stout/gtest.hpp>
+-#include <stout/none.hpp>
+-#include <stout/option.hpp>
+-#include <stout/result.hpp>
+-#include <stout/some.hpp>
+-#include <stout/try.hpp>
+-
+-TEST(Stout, Some)
+-{
+- Option<int> o1 = Some(42);
+- EXPECT_SOME(o1);
+- EXPECT_EQ(42, o1.get());
+-
+- Result<int> r1 = Some(42);
+- EXPECT_SOME(r1);
+- EXPECT_EQ(42, r1.get());
+-
+- Try<Option<int> > t1 = Some(42);
+- ASSERT_SOME(t1);
+- EXPECT_SOME(t1.get());
+- EXPECT_EQ(42, t1.get().get());
+-
+- Try<Result<int> > t2 = Some(42);
+- ASSERT_SOME(t2);
+- EXPECT_SOME(t2.get());
+- EXPECT_EQ(42, t2.get().get());
+-
+- Option<Result<int> > o2 = Some(42);
+- ASSERT_SOME(o2);
+- EXPECT_SOME(o2.get());
+- EXPECT_EQ(42, o2.get().get());
+-
+- Option<Result<int> > o3 = Some(Some(42));
+- ASSERT_SOME(o3);
+- EXPECT_SOME(o3.get());
+- EXPECT_EQ(42, o3.get().get());
+-
+- Result<Option<int> > r2 = Some(42);
+- ASSERT_SOME(r2);
+- EXPECT_SOME(r2.get());
+- EXPECT_EQ(42, r2.get().get());
+-
+- Result<Option<int> > r3 = Some(Some(42));
+- ASSERT_SOME(r3);
+- EXPECT_SOME(r3.get());
+- EXPECT_EQ(42, r3.get().get());
+-
+- Option<std::string> o4 = Some("hello");
+- EXPECT_SOME(o4);
+- EXPECT_EQ("hello", o4.get());
+-
+- Result<std::string> r4 = Some("world");
+- EXPECT_SOME(r4);
+- EXPECT_EQ("world", r4.get());
+-
+- std::map<std::string, Option<std::string> > values;
+- values["no-debug"] = None();
+- values["debug"] = None();
+- values["debug"] = Some("true");
+- values["debug"] = Some("false");
+- values["name"] = Some("frank");
+-}
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/strings_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/strings_tests.cpp
+deleted file mode 100644
+index b5a233f..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/strings_tests.cpp
++++ /dev/null
+@@ -1,298 +0,0 @@
+-#include <gtest/gtest.h>
+-
+-#include <gmock/gmock.h>
+-
+-#include <map>
+-#include <string>
+-#include <vector>
+-
+-#include <stout/format.hpp>
+-#include <stout/gtest.hpp>
+-#include <stout/strings.hpp>
+-#include <stout/try.hpp>
+-
+-using std::map;
+-using std::string;
+-using std::vector;
+-
+-
+-TEST(StringsTest, Format)
+-{
+- Try<std::string> result = strings::format("%s %s", "hello", "world");
+- ASSERT_SOME(result);
+- EXPECT_EQ("hello world", result.get());
+-
+- result = strings::format("hello %d", 42);
+- ASSERT_SOME(result);
+- EXPECT_EQ("hello 42", result.get());
+-
+- result = strings::format("hello %s", "fourty-two");
+- ASSERT_SOME(result);
+- EXPECT_EQ("hello fourty-two", result.get());
+-
+- string hello = "hello";
+-
+- result = strings::format("%s %s", hello, "fourty-two");
+- ASSERT_SOME(result);
+- EXPECT_EQ("hello fourty-two", result.get());
+-}
+-
+-
+-TEST(StringsTest, Remove)
+-{
+- EXPECT_EQ("heo word", strings::remove("hello world", "l"));
+- EXPECT_EQ("hel world", strings::remove("hello world", "lo"));
+- EXPECT_EQ("home/", strings::remove("/home/", "/", strings::PREFIX));
+- EXPECT_EQ("/home", strings::remove("/home/", "/", strings::SUFFIX));
+-}
+-
+-
+-TEST(StringsTest, Replace)
+-{
+- EXPECT_EQ("hello*", strings::replace("hello/", "/", "*"));
+- EXPECT_EQ("*hello", strings::replace("/hello", "/", "*"));
+- EXPECT_EQ("*hello*world*", strings::replace("/hello/world/", "/", "*"));
+- EXPECT_EQ("*", strings::replace("/", "/", "*"));
+- EXPECT_EQ("hello world", strings::replace("hello world", "/", "*"));
+- EXPECT_EQ("***1***2***3***", strings::replace("/1/2/3/", "/", "***"));
+- EXPECT_EQ("123", strings::replace("/1/2/3/", "/", ""));
+- EXPECT_EQ("/1/2/3**", strings::replace("***1***2***3**", "***", "/"));
+- EXPECT_EQ("/1/2/3/", strings::replace("/1/2/3/", "", "*"));
+-}
+-
+-
+-TEST(StringsTest, Trim)
+-{
+- EXPECT_EQ("", strings::trim("", " "));
+- EXPECT_EQ("", strings::trim(" ", " "));
+- EXPECT_EQ("hello world", strings::trim("hello world", " "));
+- EXPECT_EQ("hello world", strings::trim(" hello world", " "));
+- EXPECT_EQ("hello world", strings::trim("hello world ", " "));
+- EXPECT_EQ("hello world", strings::trim(" hello world ", " "));
+- EXPECT_EQ("hello world", strings::trim(" \t hello world\t ", " \t"));
+- EXPECT_EQ("hello world", strings::trim(" \t hello world\t \n\r "));
+-}
+-
+-
+-TEST(StringsTest, Tokenize)
+-{
+- vector<string> tokens = strings::tokenize("hello world, what's up?", " ");
+- ASSERT_EQ(4u, tokens.size());
+- EXPECT_EQ("hello", tokens[0]);
+- EXPECT_EQ("world,", tokens[1]);
+- EXPECT_EQ("what's", tokens[2]);
+- EXPECT_EQ("up?", tokens[3]);
+-}
+-
+-
+-TEST(StringsTest, TokenizeStringWithDelimsAtStart)
+-{
+- vector<string> tokens = strings::tokenize(" hello world, what's up?", " ");
+- ASSERT_EQ(4u, tokens.size());
+- EXPECT_EQ("hello", tokens[0]);
+- EXPECT_EQ("world,", tokens[1]);
+- EXPECT_EQ("what's", tokens[2]);
+- EXPECT_EQ("up?", tokens[3]);
+-}
+-
+-
+-TEST(StringsTest, TokenizeStringWithDelimsAtEnd)
+-{
+- vector<string> tokens = strings::tokenize("hello world, what's up? ", " ");
+- ASSERT_EQ(4u, tokens.size());
+- EXPECT_EQ("hello", tokens[0]);
+- EXPECT_EQ("world,", tokens[1]);
+- EXPECT_EQ("what's", tokens[2]);
+- EXPECT_EQ("up?", tokens[3]);
+-}
+-
+-
+-TEST(StringsTest, TokenizeStringWithDelimsAtStartAndEnd)
+-{
+- vector<string> tokens = strings::tokenize(" hello world, what's up? ", " ");
+- ASSERT_EQ(4u, tokens.size());
+- EXPECT_EQ("hello", tokens[0]);
+- EXPECT_EQ("world,", tokens[1]);
+- EXPECT_EQ("what's", tokens[2]);
+- EXPECT_EQ("up?", tokens[3]);
+-}
+-
+-
+-TEST(StringsTest, TokenizeWithMultipleDelims)
+-{
+- vector<string> tokens = strings::tokenize("hello\tworld, \twhat's up?",
+- " \t");
+- ASSERT_EQ(4u, tokens.size());
+- EXPECT_EQ("hello", tokens[0]);
+- EXPECT_EQ("world,", tokens[1]);
+- EXPECT_EQ("what's", tokens[2]);
+- EXPECT_EQ("up?", tokens[3]);
+-}
+-
+-
+-TEST(StringsTest, TokenizeEmptyString)
+-{
+- vector<string> tokens = strings::tokenize("", " ");
+- ASSERT_EQ(0u, tokens.size());
+-}
+-
+-
+-TEST(StringsTest, TokenizeDelimOnlyString)
+-{
+- vector<string> tokens = strings::tokenize(" ", " ");
+- ASSERT_EQ(0u, tokens.size());
+-}
+-
+-
+-TEST(StringsTest, TokenizeNullByteDelim)
+-{
+- string s;
+- s.push_back('\0');
+- s.push_back('\0');
+- s.push_back('\0');
+- s.push_back('h');
+- s.push_back('e');
+- s.push_back('l');
+- s.push_back('l');
+- s.push_back('o');
+- s.push_back('\0');
+- s.push_back('\0');
+- s.push_back('\0');
+- s.push_back('\0');
+- s.push_back('\0');
+- s.push_back('\0');
+- s.push_back('w');
+- s.push_back('o');
+- s.push_back('r');
+- s.push_back('l');
+- s.push_back('d');
+- s.push_back('\0');
+- s.push_back('\0');
+- s.push_back('\0');
+-
+- vector<string> tokens = strings::tokenize(s, string(1, '\0'));
+-
+- ASSERT_EQ(2u, tokens.size());
+- EXPECT_EQ("hello", tokens[0]);
+- EXPECT_EQ("world", tokens[1]);
+-}
+-
+-
+-TEST(StringsTest, SplitEmptyString)
+-{
+- vector<string> tokens = strings::split("", ",");
+- ASSERT_EQ(1u, tokens.size());
+- EXPECT_EQ("", tokens[0]);
+-}
+-
+-
+-TEST(StringsTest, SplitDelimOnlyString)
+-{
+- vector<string> tokens = strings::split(",,,", ",");
+- ASSERT_EQ(4u, tokens.size());
+- EXPECT_EQ("", tokens[0]);
+- EXPECT_EQ("", tokens[1]);
+- EXPECT_EQ("", tokens[2]);
+- EXPECT_EQ("", tokens[3]);
+-}
+-
+-
+-TEST(StringsTest, Split)
+-{
+- vector<string> tokens = strings::split("foo,bar,,baz", ",");
+- ASSERT_EQ(4u, tokens.size());
+- EXPECT_EQ("foo", tokens[0]);
+- EXPECT_EQ("bar", tokens[1]);
+- EXPECT_EQ("", tokens[2]);
+- EXPECT_EQ("baz", tokens[3]);
+-}
+-
+-
+-TEST(StringsTest, SplitStringWithDelimsAtStart)
+-{
+- vector<string> tokens = strings::split(",,foo,bar,,baz", ",");
+- ASSERT_EQ(6u, tokens.size());
+- EXPECT_EQ("", tokens[0]);
+- EXPECT_EQ("", tokens[1]);
+- EXPECT_EQ("foo", tokens[2]);
+- EXPECT_EQ("bar", tokens[3]);
+- EXPECT_EQ("", tokens[4]);
+- EXPECT_EQ("baz", tokens[5]);
+-}
+-
+-
+-TEST(StringsTest, SplitStringWithDelimsAtEnd)
+-{
+- vector<string> tokens = strings::split("foo,bar,,baz,,", ",");
+- ASSERT_EQ(6u, tokens.size());
+- EXPECT_EQ("foo", tokens[0]);
+- EXPECT_EQ("bar", tokens[1]);
+- EXPECT_EQ("", tokens[2]);
+- EXPECT_EQ("baz", tokens[3]);
+- EXPECT_EQ("", tokens[4]);
+- EXPECT_EQ("", tokens[5]);
+-}
+-
+-
+-TEST(StringsTest, SplitStringWithDelimsAtStartAndEnd)
+-{
+- vector<string> tokens = strings::split(",,foo,bar,,", ",");
+- ASSERT_EQ(6u, tokens.size());
+- EXPECT_EQ("", tokens[0]);
+- EXPECT_EQ("", tokens[1]);
+- EXPECT_EQ("foo", tokens[2]);
+- EXPECT_EQ("bar", tokens[3]);
+- EXPECT_EQ("", tokens[4]);
+- EXPECT_EQ("", tokens[5]);
+-}
+-
+-
+-TEST(StringsTest, SplitWithMultipleDelims)
+-{
+- vector<string> tokens = strings::split("foo.bar,.,.baz.", ",.");
+- ASSERT_EQ(7u, tokens.size());
+- EXPECT_EQ("foo", tokens[0]);
+- EXPECT_EQ("bar", tokens[1]);
+- EXPECT_EQ("", tokens[2]);
+- EXPECT_EQ("", tokens[3]);
+- EXPECT_EQ("", tokens[4]);
+- EXPECT_EQ("baz", tokens[5]);
+- EXPECT_EQ("", tokens[6]);
+-}
+-
+-
+-TEST(StringsTest, Pairs)
+-{
+- map<string, vector<string> > pairs = strings::pairs("one=1,two=2", ",", "=");
+- ASSERT_EQ(2u, pairs.size());
+- ASSERT_EQ(1u, pairs.count("one"));
+- ASSERT_EQ(1u, pairs["one"].size());
+- EXPECT_EQ("1", pairs["one"].front());
+- ASSERT_EQ(1u, pairs.count("two"));
+- ASSERT_EQ(1u, pairs["two"].size());
+- EXPECT_EQ("2", pairs["two"].front());
+-
+- pairs = strings::pairs("foo=1;bar=2;baz;foo=3;bam=1=2", ";&", "=");
+- ASSERT_EQ(2, pairs.size());
+- ASSERT_EQ(1u, pairs.count("foo"));
+- ASSERT_EQ(2u, pairs["foo"].size());
+- ASSERT_EQ("1", pairs["foo"].front());
+- ASSERT_EQ("3", pairs["foo"].back());
+- ASSERT_EQ(1u, pairs.count("bar"));
+- ASSERT_EQ("2", pairs["bar"].front());
+-}
+-
+-
+-TEST(StringsTest, StartsWith)
+-{
+- EXPECT_TRUE(strings::startsWith("hello world", "hello"));
+- EXPECT_FALSE(strings::startsWith("hello world", "no"));
+- EXPECT_FALSE(strings::startsWith("hello world", "ello"));
+-}
+-
+-
+-TEST(StringsTest, Contains)
+-{
+- EXPECT_TRUE(strings::contains("hello world", "world"));
+- EXPECT_FALSE(strings::contains("hello world", "no"));
+-}
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/thread_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/thread_tests.cpp
+deleted file mode 100644
+index 7519b12..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/thread_tests.cpp
++++ /dev/null
+@@ -1,26 +0,0 @@
+-#include <gtest/gtest.h>
+-
+-#include <string>
+-
+-#include <stout/thread.hpp>
+-
+-TEST(Thread, local)
+-{
+- ThreadLocal<std::string>* _s_ = new ThreadLocal<std::string>();
+-
+- std::string* s = new std::string();
+-
+- ASSERT_TRUE(*(_s_) == NULL);
+-
+- (*_s_) = s;
+-
+- ASSERT_TRUE(*(_s_) == s);
+- ASSERT_FALSE(*(_s_) == NULL);
+-
+- (*_s_) = NULL;
+-
+- ASSERT_TRUE(*(_s_) == NULL);
+-
+- delete s;
+- delete _s_;
+-}
+diff --git a/3rdparty/libprocess/3rdparty/stout/tests/uuid_tests.cpp b/3rdparty/libprocess/3rdparty/stout/tests/uuid_tests.cpp
+deleted file mode 100644
+index ad1d986..0000000
+--- a/3rdparty/libprocess/3rdparty/stout/tests/uuid_tests.cpp
++++ /dev/null
+@@ -1,37 +0,0 @@
+-#include <gtest/gtest.h>
+-
+-#include <gmock/gmock.h>
+-
+-#include <string>
+-
+-#include <stout/uuid.hpp>
+-
+-using std::string;
+-
+-
+-TEST(UUIDTest, test)
+-{
+- UUID uuid1 = UUID::random();
+- UUID uuid2 = UUID::fromBytes(uuid1.toBytes());
+- UUID uuid3 = uuid2;
+-
+- EXPECT_EQ(uuid1, uuid2);
+- EXPECT_EQ(uuid2, uuid3);
+- EXPECT_EQ(uuid1, uuid3);
+-
+- string bytes1 = uuid1.toBytes();
+- string bytes2 = uuid2.toBytes();
+- string bytes3 = uuid3.toBytes();
+-
+- EXPECT_EQ(bytes1, bytes2);
+- EXPECT_EQ(bytes2, bytes3);
+- EXPECT_EQ(bytes1, bytes3);
+-
+- string string1 = uuid1.toString();
+- string string2 = uuid2.toString();
+- string string3 = uuid3.toString();
+-
+- EXPECT_EQ(string1, string2);
+- EXPECT_EQ(string2, string3);
+- EXPECT_EQ(string1, string3);
+-}
+diff --git a/3rdparty/libprocess/examples/example.cpp b/3rdparty/libprocess/examples/example.cpp
+deleted file mode 100644
+index 3fb4ef5..0000000
+--- a/3rdparty/libprocess/examples/example.cpp
++++ /dev/null
+@@ -1,121 +0,0 @@
+-#include <iostream>
+-#include <sstream>
+-
+-#include <process/defer.hpp>
+-#include <process/dispatch.hpp>
+-#include <process/future.hpp>
+-#include <process/http.hpp>
+-#include <process/process.hpp>
+-
+-using namespace process;
+-
+-using namespace process::http;
+-
+-using std::string;
+-
+-class MyProcess : public Process<MyProcess>
+-{
+-public:
+- MyProcess() {}
+- virtual ~MyProcess() {}
+-
+- Future<int> func1()
+- {
+- promise.future().onAny(
+- defer([=] (const Future<int>& future) {
+- terminate(self());
+- }));
+- return promise.future();
+- }
+-
+- void func2(int i)
+- {
+- promise.set(i);
+- }
+-
+- Future<Response> vars(const Request& request)
+- {
+- string body = "... vars here ...";
+- OK response;
+- response.headers["Content-Type"] = "text/plain";
+- std::ostringstream out;
+- out << body.size();
+- response.headers["Content-Length"] = out.str();
+- response.body = body;
+- return response;
+- }
+-
+- void stop(const UPID& from, const string& body)
+- {
+- terminate(self());
+- }
+-
+-protected:
+- virtual void initialize()
+- {
+-// route("/vars", &MyProcess::vars);
+- route("/vars", [=] (const Request& request) {
+- string body = "... vars here ...";
+- OK response;
+- response.headers["Content-Type"] = "text/plain";
+- std::ostringstream out;
+- out << body.size();
+- response.headers["Content-Length"] = out.str();
+- response.body = body;
+- return response;
+- });
+-
+-// install("stop", &MyProcess::stop);
+- install("stop", [=] (const UPID& from, const string& body) {
+- terminate(self());
+- });
+- }
+-
+-private:
+- Promise<int> promise;
+-};
+-
+-
+-int main(int argc, char** argv)
+-{
+- MyProcess process;
+- PID<MyProcess> pid = spawn(&process);
+-
+- PID<> pid2 = pid;
+-
+-// --------------------------------------
+-
+-// Future<int> future = dispatch(pid, &MyProcess::func1);
+-// dispatch(pid, &MyProcess::func2, 42);
+-
+-// std::cout << future.get() << std::endl;
+-
+-// post(pid, "stop");
+-
+-// --------------------------------------
+-
+-// Promise<bool> p;
+-
+-// dispatch(pid, &MyProcess::func1)
+-// .then([=, &p] (int i) {
+-// p.set(i == 42);
+-// return p.future();
+-// })
+-// .then([=] (bool b) {
+-// if (b) {
+-// post(pid, "stop");
+-// }
+-// return true; // No Future<void>.
+-// });
+-
+-// dispatch(pid, &MyProcess::func2, 42);
+-
+-// --------------------------------------
+-
+- dispatch(pid, &MyProcess::func1);
+- dispatch(pid, &MyProcess::func2, 42);
+-
+-
+- wait(pid);
+- return 0;
+-}
+diff --git a/3rdparty/libprocess/include/process/async.hpp b/3rdparty/libprocess/include/process/async.hpp
+deleted file mode 100644
+index 8fa2771..0000000
+--- a/3rdparty/libprocess/include/process/async.hpp
++++ /dev/null
+@@ -1,231 +0,0 @@
+-#ifndef __ASYNC_HPP__
+-#define __ASYNC_HPP__
+-
+-#include <process/dispatch.hpp>
+-#include <process/future.hpp>
+-#include <process/id.hpp>
+-#include <process/process.hpp>
+-
+-#include <tr1/functional>
+-
+-namespace process {
+-
+-// TODO(vinod): Merge this into ExecutorProcess.
+-// TODO(vinod): Add support for void functions. Currently this is tricky,
+-// because Future<void> is not supported.
+-class AsyncExecutorProcess : public Process<AsyncExecutorProcess>
+-{
+-private:
+- friend class AsyncExecutor;
+-
+- AsyncExecutorProcess() : ProcessBase(ID::generate("__async_executor__")) {}
+- virtual ~AsyncExecutorProcess() {}
+-
+- // Not copyable, not assignable.
+- AsyncExecutorProcess(const AsyncExecutorProcess&);
+- AsyncExecutorProcess& operator = (const AsyncExecutorProcess&);
+-
+- template<typename F>
+- typename std::tr1::result_of<F(void)>::type execute(
+- const F& f)
+- {
+- terminate(self()); // Terminate this process after the function returns.
+- return f();
+- }
+-
+- // TODO(vinod): Use boost macro enumerations.
+- template<typename F, typename A1>
+- typename std::tr1::result_of<F(A1)>::type execute(
+- const F& f, A1 a1)
+- {
+- terminate(self()); // Terminate this process after the function returns.
+- return f(a1);
+- }
+-
+- template<typename F, typename A1, typename A2>
+- typename std::tr1::result_of<F(A1, A2)>::type execute(
+- const F& f, A1 a1, A2 a2)
+- {
+- terminate(self()); // Terminate this process after the function returns.
+- return f(a1, a2);
+- }
+-
+- template<typename F, typename A1, typename A2, typename A3>
+- typename std::tr1::result_of<F(A1, A2, A3)>::type execute(
+- const F& f, A1 a1, A2 a2, A3 a3)
+- {
+- terminate(self()); // Terminate this process after the function returns.
+- return f(a1, a2, a3);
+- }
+-
+- template<typename F, typename A1, typename A2, typename A3, typename A4>
+- typename std::tr1::result_of<F(A1, A2, A3, A4)>::type execute(
+- const F& f, A1 a1, A2 a2, A3 a3, A4 a4)
+- {
+- terminate(self()); // Terminate this process after the function returns.
+- return f(a1, a2, a3, a4);
+- }
+-};
+-
+-
+-// This is a wrapper around AsyncExecutorProcess.
+-class AsyncExecutor
+-{
+-private:
+- // Declare async functions as friends.
+- template<typename F>
+- friend Future<typename std::tr1::result_of<F(void)>::type> async(
+- const F& f);
+-
+- template<typename F, typename A1>
+- friend Future<typename std::tr1::result_of<F(A1)>::type> async(
+- const F& f, A1 a1);
+-
+- template<typename F, typename A1, typename A2>
+- friend Future<typename std::tr1::result_of<F(A1, A2)>::type> async(
+- const F& f, A1 a1, A2 a2);
+-
+- template<typename F, typename A1, typename A2, typename A3>
+- friend Future<typename std::tr1::result_of<F(A1, A2, A3)>::type> async(
+- const F& f, A1 a1, A2 a2, A3 a3);
+-
+- template<typename F, typename A1, typename A2, typename A3, typename A4>
+- friend Future<typename std::tr1::result_of<F(A1, A2, A3, A4)>::type> async(
+- const F& f, A1 a1, A2 a2, A3 a3, A4 a4);
+-
+- AsyncExecutor()
+- {
+- process = new AsyncExecutorProcess();
+- spawn(process, true); // Automatically GC.
+- }
+-
+- virtual ~AsyncExecutor() {}
+-
+- // Not copyable, not assignable.
+- AsyncExecutor(const AsyncExecutor&);
+- AsyncExecutor& operator = (const AsyncExecutor&);
+-
+- template<typename F>
+- Future<typename std::tr1::result_of<F(void)>::type> execute(
+- const F& f)
+- {
+- // Necessary to disambiguate.
+- typedef typename std::tr1::result_of<F(void)>::type
+- (AsyncExecutorProcess::*R)(const F&);
+-
+- return dispatch(process,
+- static_cast<R>(&AsyncExecutorProcess::execute),
+- f);
+- }
+-
+- // TODO(vinod): Use boost macro enumerations.
+- template<typename F, typename A1>
+- Future<typename std::tr1::result_of<F(A1)>::type> execute(
+- const F& f, A1 a1)
+- {
+- // Necessary to disambiguate.
+- typedef typename std::tr1::result_of<F(A1)>::type
+- (AsyncExecutorProcess::*R)(const F&, A1);
+-
+- return dispatch(process,
+- static_cast<R>(&AsyncExecutorProcess::execute),
+- f,
+- a1);
+- }
+-
+- template<typename F, typename A1, typename A2>
+- Future<typename std::tr1::result_of<F(A1, A2)>::type> execute(
+- const F& f, A1 a1, A2 a2)
+- {
+- // Necessary to disambiguate.
+- typedef typename std::tr1::result_of<F(A1, A2)>::type
+- (AsyncExecutorProcess::*R)(const F&, A1, A2);
+-
+- return dispatch(process,
+- static_cast<R>(&AsyncExecutorProcess::execute),
+- f,
+- a1,
+- a2);
+- }
+-
+- template<typename F, typename A1, typename A2, typename A3>
+- Future<typename std::tr1::result_of<F(A1, A2, A3)>::type> execute(
+- const F& f, A1 a1, A2 a2, A3 a3)
+- {
+- // Necessary to disambiguate.
+- typedef typename std::tr1::result_of<F(A1, A2, A3)>::type
+- (AsyncExecutorProcess::*R)(const F&, A1, A2, A3);
+-
+- return dispatch(process,
+- static_cast<R>(&AsyncExecutorProcess::execute),
+- f,
+- a1,
+- a2,
+- a3);
+- }
+-
+- template<typename F, typename A1, typename A2, typename A3, typename A4>
+- Future<typename std::tr1::result_of<F(A1, A2, A3, A4)>::type> execute(
+- const F& f, A1 a1, A2 a2, A3 a3, A4 a4)
+- {
+- // Necessary to disambiguate.
+- typedef typename std::tr1::result_of<F(A1, A2, A3, A4)>::type
+- (AsyncExecutorProcess::*R)(const F&, A1, A2, A3, A4);
+-
+- return dispatch(process,
+- static_cast<R>(&AsyncExecutorProcess::execute),
+- f,
+- a1,
+- a2,
+- a3,
+- a4);
+- }
+-
+- AsyncExecutorProcess* process;
+-};
+-
+-
+-// Provides an abstraction for asynchronously executing a function.
+-// TODO(vinod): Use boost macro to enumerate arguments/params.
+-template<typename F>
+-Future<typename std::tr1::result_of<F(void)>::type>
+- async(const F& f)
+-{
+- return AsyncExecutor().execute(f);
+-}
+-
+-
+-template<typename F, typename A1>
+-Future<typename std::tr1::result_of<F(A1)>::type>
+- async(const F& f, A1 a1)
+-{
+- return AsyncExecutor().execute(f, a1);
+-}
+-
+-
+-template<typename F, typename A1, typename A2>
+-Future<typename std::tr1::result_of<F(A1, A2)>::type>
+- async(const F& f, A1 a1, A2 a2)
+-{
+- return AsyncExecutor().execute(f, a1, a2);
+-}
+-
+-
+-template<typename F, typename A1, typename A2, typename A3>
+-Future<typename std::tr1::result_of<F(A1, A2, A3)>::type>
+- async(const F& f, A1 a1, A2 a2, A3 a3)
+-{
+- return AsyncExecutor().execute(f, a1, a2, a3);
+-}
+-
+-
+-template<typename F, typename A1, typename A2, typename A3, typename A4>
+-Future<typename std::tr1::result_of<F(A1, A2, A3, A4)>::type>
+- async(const F& f, A1 a1, A2 a2, A3 a3, A4 a4)
+-{
+- return AsyncExecutor().execute(f, a1, a2, a3, a4);
+-}
+-
+-} // namespace process {
+-
+-#endif // __ASYNC_HPP__
+diff --git a/3rdparty/libprocess/include/process/clock.hpp b/3rdparty/libprocess/include/process/clock.hpp
+deleted file mode 100644
+index 82ae3c6..0000000
+--- a/3rdparty/libprocess/include/process/clock.hpp
++++ /dev/null
+@@ -1,32 +0,0 @@
+-#ifndef __PROCESS_CLOCK_HPP__
+-#define __PROCESS_CLOCK_HPP__
+-
+-#include <process/time.hpp>
+-
+-#include <stout/duration.hpp>
+-
+-namespace process {
+-
+-// Forward declarations.
+-class ProcessBase;
+-class Time;
+-
+-class Clock
+-{
+-public:
+- static Time now();
+- static Time now(ProcessBase* process);
+- static void pause();
+- static bool paused();
+- static void resume();
+- static void advance(const Duration& duration);
+- static void advance(ProcessBase* process, const Duration& duration);
+- static void update(const Time& time);
+- static void update(ProcessBase* process, const Time& time);
+- static void order(ProcessBase* from, ProcessBase* to);
+- static void settle();
+-};
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_CLOCK_HPP__
+diff --git a/3rdparty/libprocess/include/process/collect.hpp b/3rdparty/libprocess/include/process/collect.hpp
+deleted file mode 100644
+index 27e2729..0000000
+--- a/3rdparty/libprocess/include/process/collect.hpp
++++ /dev/null
+@@ -1,235 +0,0 @@
+-#ifndef __PROCESS_COLLECT_HPP__
+-#define __PROCESS_COLLECT_HPP__
+-
+-#include <assert.h>
+-
+-#include <list>
+-
+-#include <process/defer.hpp>
+-#include <process/delay.hpp>
+-#include <process/future.hpp>
+-#include <process/process.hpp>
+-#include <process/timeout.hpp>
+-
+-#include <stout/none.hpp>
+-#include <stout/option.hpp>
+-
+-// TODO(bmahler): Move these into a futures.hpp header to group Future
+-// related utilities.
+-
+-namespace process {
+-
+-// Waits on each future in the specified list and returns the list of
+-// resulting values in the same order. If any future is discarded then
+-// the result will be a failure. Likewise, if any future fails then
+-// the result future will be a failure.
+-template <typename T>
+-Future<std::list<T> > collect(
+- std::list<Future<T> >& futures,
+- const Option<Timeout>& timeout = None());
+-
+-
+-// Waits on each future in the specified set and returns the list of
+-// non-pending futures. On timeout, the result will be a failure.
+-template <typename T>
+-Future<std::list<Future<T> > > await(
+- std::list<Future<T> >& futures,
+- const Option<Timeout>& timeout = None());
+-
+-
+-namespace internal {
+-
+-template <typename T>
+-class CollectProcess : public Process<CollectProcess<T> >
+-{
+-public:
+- CollectProcess(
+- const std::list<Future<T> >& _futures,
+- const Option<Timeout>& _timeout,
+- Promise<std::list<T> >* _promise)
+- : futures(_futures),
+- timeout(_timeout),
+- promise(_promise),
+- ready(0) {}
+-
+- virtual ~CollectProcess()
+- {
+- delete promise;
+- }
+-
+- virtual void initialize()
+- {
+- // Stop this nonsense if nobody cares.
+- promise->future().onDiscarded(defer(this, &CollectProcess::discarded));
+-
+- // Only wait as long as requested.
+- if (timeout.isSome()) {
+- delay(timeout.get().remaining(), this, &CollectProcess::timedout);
+- }
+-
+- typename std::list<Future<T> >::const_iterator iterator;
+- for (iterator = futures.begin(); iterator != futures.end(); ++iterator) {
+- (*iterator).onAny(
+- defer(this, &CollectProcess::waited, std::tr1::placeholders::_1));
+- }
+- }
+-
+-private:
+- void discarded()
+- {
+- terminate(this);
+- }
+-
+- void timedout()
+- {
+- // Need to discard all of the futures so any of their associated
+- // resources can get properly cleaned up.
+- typename std::list<Future<T> >::const_iterator iterator;
+- for (iterator = futures.begin(); iterator != futures.end(); ++iterator) {
+- Future<T> future = *iterator; // Need a non-const copy to discard.
+- future.discard();
+- }
+-
+- promise->fail("Collect failed: timed out");
+- terminate(this);
+- }
+-
+- void waited(const Future<T>& future)
+- {
+- if (future.isFailed()) {
+- promise->fail("Collect failed: " + future.failure());
+- terminate(this);
+- } else if (future.isDiscarded()) {
+- promise->fail("Collect failed: future discarded");
+- terminate(this);
+- } else {
+- assert(future.isReady());
+- ready += 1;
+- if (ready == futures.size()) {
+- std::list<T> values;
+- foreach (const Future<T>& future, futures) {
+- values.push_back(future.get());
+- }
+- promise->set(values);
+- terminate(this);
+- }
+- }
+- }
+-
+- const std::list<Future<T> > futures;
+- const Option<Timeout> timeout;
+- Promise<std::list<T> >* promise;
+- size_t ready;
+-};
+-
+-
+-template <typename T>
+-class AwaitProcess : public Process<AwaitProcess<T> >
+-{
+-public:
+- AwaitProcess(
+- const std::list<Future<T> >& _futures,
+- const Option<Timeout>& _timeout,
+- Promise<std::list<Future<T> > >* _promise)
+- : futures(_futures),
+- timeout(_timeout),
+- promise(_promise),
+- ready(0) {}
+-
+- virtual ~AwaitProcess()
+- {
+- delete promise;
+- }
+-
+- virtual void initialize()
+- {
+- // Stop this nonsense if nobody cares.
+- promise->future().onDiscarded(defer(this, &AwaitProcess::discarded));
+-
+- // Only wait as long as requested.
+- if (timeout.isSome()) {
+- delay(timeout.get().remaining(), this, &AwaitProcess::timedout);
+- }
+-
+- typename std::list<Future<T> >::const_iterator iterator;
+- for (iterator = futures.begin(); iterator != futures.end(); ++iterator) {
+- (*iterator).onAny(
+- defer(this, &AwaitProcess::waited, std::tr1::placeholders::_1));
+- }
+- }
+-
+-private:
+- void discarded()
+- {
+- terminate(this);
+- }
+-
+- void timedout()
+- {
+- // Need to discard all of the futures so any of their associated
+- // resources can get properly cleaned up.
+- typename std::list<Future<T> >::const_iterator iterator;
+- for (iterator = futures.begin(); iterator != futures.end(); ++iterator) {
+- Future<T> future = *iterator; // Need a non-const copy to discard.
+- future.discard();
+- }
+-
+- promise->fail("Collect failed: timed out");
+- terminate(this);
+- }
+-
+- void waited(const Future<T>& future)
+- {
+- assert(!future.isPending());
+-
+- ready += 1;
+- if (ready == futures.size()) {
+- promise->set(futures);
+- terminate(this);
+- }
+- }
+-
+- const std::list<Future<T> > futures;
+- const Option<Timeout> timeout;
+- Promise<std::list<Future<T> > >* promise;
+- size_t ready;
+-};
+-
+-} // namespace internal {
+-
+-
+-template <typename T>
+-inline Future<std::list<T> > collect(
+- std::list<Future<T> >& futures,
+- const Option<Timeout>& timeout)
+-{
+- if (futures.empty()) {
+- return std::list<T>();
+- }
+-
+- Promise<std::list<T> >* promise = new Promise<std::list<T> >();
+- Future<std::list<T> > future = promise->future();
+- spawn(new internal::CollectProcess<T>(futures, timeout, promise), true);
+- return future;
+-}
+-
+-
+-template <typename T>
+-inline Future<std::list<Future<T> > > await(
+- std::list<Future<T> >& futures,
+- const Option<Timeout>& timeout)
+-{
+- if (futures.empty()) {
+- return futures;
+- }
+-
+- Promise<std::list<Future<T> > >* promise =
+- new Promise<std::list<Future<T> > >();
+- Future<std::list<Future<T> > > future = promise->future();
+- spawn(new internal::AwaitProcess<T>(futures, timeout, promise), true);
+- return future;
+-}
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_COLLECT_HPP__
+diff --git a/3rdparty/libprocess/include/process/defer.hpp b/3rdparty/libprocess/include/process/defer.hpp
+deleted file mode 100644
+index 1eb770b..0000000
+--- a/3rdparty/libprocess/include/process/defer.hpp
++++ /dev/null
+@@ -1,438 +0,0 @@
+-#ifndef __PROCESS_DEFER_HPP__
+-#define __PROCESS_DEFER_HPP__
+-
+-#include <tr1/functional>
+-
+-#include <process/deferred.hpp>
+-#include <process/dispatch.hpp>
+-#include <process/executor.hpp>
+-
+-#include <stout/preprocessor.hpp>
+-
+-namespace process {
+-
+-// The defer mechanism is very similar to the dispatch mechanism (see
+-// dispatch.hpp), however, rather than scheduling the method to get
+-// invoked, the defer mechanism returns a 'Deferred' object that when
+-// invoked does the underlying dispatch. Similar to dispatch, we
+-// provide the C++11 variadic template definitions first, and then use
+-// Boost preprocessor macros to provide the actual definitions.
+-
+-
+-// First, definitions of defer for methods returning void:
+-//
+-// template <typename T, typename ...P>
+-// Deferred<void(void)> void defer(const PID<T>& pid,
+-// void (T::*method)(P...),
+-// P... p)
+-// {
+-// void (*dispatch)(const PID<T>&, void (T::*)(P...), P...) =
+-// &process::template dispatch<T, P...>;
+-
+-// return Deferred<void(void)>(
+-// std::tr1::bind(dispatch, pid, method, std::forward<P>(p)...));
+-// }
+-
+-template <typename T>
+-_Defer<void(*(PID<T>, void (T::*)(void)))
+- (const PID<T>&, void (T::*)(void))>
+-defer(const PID<T>& pid, void (T::*method)(void))
+-{
+- void (*dispatch)(const PID<T>&, void (T::*)(void)) =
+- &process::template dispatch<T>;
+- return std::tr1::bind(dispatch, pid, method);
+-}
+-
+-template <typename T>
+-_Defer<void(*(PID<T>, void (T::*)(void)))
+- (const PID<T>&, void (T::*)(void))>
+-defer(const Process<T>& process, void (T::*method)(void))
+-{
+- return defer(process.self(), method);
+-}
+-
+-template <typename T>
+-_Defer<void(*(PID<T>, void (T::*)(void)))
+- (const PID<T>&, void (T::*)(void))>
+-defer(const Process<T>* process, void (T::*method)(void))
+-{
+- return defer(process->self(), method);
+-}
+-
+-#define TEMPLATE(Z, N, DATA) \
+- template <typename T, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- _Defer<void(*(PID<T>, \
+- void (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, A))) \
+- (const PID<T>&, \
+- void (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, P))> \
+- defer(const PID<T>& pid, \
+- void (T::*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- void (*dispatch)(const PID<T>&, \
+- void (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, P)) = \
+- &process::template dispatch<T, ENUM_PARAMS(N, P), ENUM_PARAMS(N, P)>; \
+- return std::tr1::bind(dispatch, pid, method, ENUM_PARAMS(N, a)); \
+- } \
+- \
+- template <typename T, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- _Defer<void(*(PID<T>, \
+- void (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, A))) \
+- (const PID<T>&, \
+- void (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, P))> \
+- defer(const Process<T>& process, \
+- void (T::*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- return defer(process.self(), method, ENUM_PARAMS(N, a)); \
+- } \
+- \
+- template <typename T, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- _Defer<void(*(PID<T>, \
+- void (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, A))) \
+- (const PID<T>&, \
+- void (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, P))> \
+- defer(const Process<T>* process, \
+- void (T::*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- return defer(process->self(), method, ENUM_PARAMS(N, a)); \
+- }
+-
+- REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
+-#undef TEMPLATE
+-
+-
+-// Next, definitions of defer for methods returning future:
+-//
+-// template <typename R, typename T, typename ...P>
+-// Deferred<Future<R>(void)> void defer(const PID<T>& pid,
+-// Future<R> (T::*method)(P...),
+-// P... p)
+-// {
+-// Future<R> (*dispatch)(const PID<T>&, Future<R> (T::*)(P...), P...) =
+-// &process::template dispatch<R, T, P...>;
+-//
+-// return Deferred<Future<R>(void)>(
+-// std::tr1::bind(dispatch, pid, method, std::forward<P>(p)...));
+-// }
+-
+-template <typename R, typename T>
+-_Defer<Future<R>(*(PID<T>, Future<R> (T::*)(void)))
+- (const PID<T>&, Future<R> (T::*)(void))>
+-defer(const PID<T>& pid, Future<R> (T::*method)(void))
+-{
+- Future<R> (*dispatch)(const PID<T>&, Future<R> (T::*)(void)) =
+- &process::template dispatch<R, T>;
+- return std::tr1::bind(dispatch, pid, method);
+-}
+-
+-template <typename R, typename T>
+-_Defer<Future<R>(*(PID<T>, Future<R> (T::*)(void)))(
+- const PID<T>&, Future<R> (T::*)(void))>
+-defer(const Process<T>& process, Future<R> (T::*method)(void))
+-{
+- return defer(process.self(), method);
+-}
+-
+-template <typename R, typename T>
+-_Defer<Future<R>(*(PID<T>, Future<R> (T::*)(void)))
+- (const PID<T>&, Future<R> (T::*)(void))>
+-defer(const Process<T>* process, Future<R> (T::*method)(void))
+-{
+- return defer(process->self(), method);
+-}
+-
+-#define TEMPLATE(Z, N, DATA) \
+- template <typename R, \
+- typename T, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- _Defer<Future<R>(*(PID<T>, \
+- Future<R> (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, A))) \
+- (const PID<T>&, \
+- Future<R> (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, P))> \
+- defer(const PID<T>& pid, \
+- Future<R> (T::*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- Future<R> (*dispatch)(const PID<T>&, \
+- Future<R> (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, P)) = \
+- &process::template dispatch<R, T, ENUM_PARAMS(N, P), ENUM_PARAMS(N, P)>; \
+- return std::tr1::bind(dispatch, pid, method, ENUM_PARAMS(N, a)); \
+- } \
+- \
+- template <typename R, \
+- typename T, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- _Defer<Future<R>(*(PID<T>, \
+- Future<R> (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, A))) \
+- (const PID<T>&, \
+- Future<R> (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, P))> \
+- defer(const Process<T>& process, \
+- Future<R> (T::*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- return defer(process.self(), method, ENUM_PARAMS(N, a)); \
+- } \
+- \
+- template <typename R, \
+- typename T, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- _Defer<Future<R>(*(PID<T>, \
+- Future<R> (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, A))) \
+- (const PID<T>&, \
+- Future<R> (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, P))> \
+- defer(const Process<T>* process, \
+- Future<R> (T::*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- return defer(process->self(), method, ENUM_PARAMS(N, a)); \
+- }
+-
+- REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
+-#undef TEMPLATE
+-
+-
+-// Next, definitions of defer for methods returning a value:
+-//
+-// template <typename R, typename T, typename ...P>
+-// Deferred<Future<R>(void)> void defer(const PID<T>& pid,
+-// R (T::*method)(P...),
+-// P... p)
+-// {
+-// Future<R> (*dispatch)(const PID<T>&, R (T::*)(P...), P...) =
+-// &process::template dispatch<R, T, P...>;
+-//
+-// return Deferred<Future<R>(void)>(
+-// std::tr1::bind(dispatch, pid, method, std::forward<P>(p)...));
+-// }
+-
+-template <typename R, typename T>
+-_Defer<Future<R>(*(PID<T>, R (T::*)(void)))
+- (const PID<T>&, R (T::*)(void))>
+-defer(const PID<T>& pid, R (T::*method)(void))
+-{
+- Future<R> (*dispatch)(const PID<T>&, R (T::*)(void)) =
+- &process::template dispatch<R, T>;
+- return std::tr1::bind(dispatch, pid, method);
+-}
+-
+-template <typename R, typename T>
+-_Defer<Future<R>(*(PID<T>, R (T::*)(void)))
+- (const PID<T>&, R (T::*)(void))>
+-defer(const Process<T>& process, R (T::*method)(void))
+-{
+- return defer(process.self(), method);
+-}
+-
+-template <typename R, typename T>
+-_Defer<Future<R>(*(PID<T>, R (T::*)(void)))
+- (const PID<T>&, R (T::*)(void))>
+-defer(const Process<T>* process, R (T::*method)(void))
+-{
+- return defer(process->self(), method);
+-}
+-
+-#define TEMPLATE(Z, N, DATA) \
+- template <typename R, \
+- typename T, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- _Defer<Future<R>(*(PID<T>, \
+- R (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, A))) \
+- (const PID<T>&, \
+- R (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, P))> \
+- defer(const PID<T>& pid, \
+- R (T::*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- Future<R> (*dispatch)(const PID<T>&, \
+- R (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, P)) = \
+- &process::template dispatch<R, T, ENUM_PARAMS(N, P), ENUM_PARAMS(N, P)>; \
+- return std::tr1::bind(dispatch, pid, method, ENUM_PARAMS(N, a)); \
+- } \
+- \
+- template <typename R, \
+- typename T, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- _Defer<Future<R>(*(PID<T>, \
+- R (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, A))) \
+- (const PID<T>&, \
+- R (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, P))> \
+- defer(const Process<T>& process, \
+- R (T::*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- return defer(process.self(), method, ENUM_PARAMS(N, a)); \
+- } \
+- \
+- template <typename R, \
+- typename T, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- _Defer<Future<R>(*(PID<T>, \
+- R (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, A))) \
+- (const PID<T>&, \
+- R (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, P))> \
+- defer(const Process<T>* process, \
+- R (T::*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- return defer(process->self(), method, ENUM_PARAMS(N, a)); \
+- }
+-
+- REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
+-#undef TEMPLATE
+-
+-
+-namespace internal {
+-
+-inline void invoker(
+- ProcessBase* _,
+- const std::tr1::function<void(void)>& f)
+-{
+- f();
+-}
+-
+-inline void dispatcher(
+- const UPID& pid,
+- const std::tr1::function<void(void)>& f)
+-{
+- std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > invoker(
+- new std::tr1::function<void(ProcessBase*)>(
+- std::tr1::bind(&internal::invoker,
+- std::tr1::placeholders::_1,
+- f)));
+-
+- internal::dispatch(pid, invoker);
+-}
+-
+-#define TEMPLATE(Z, N, DATA) \
+- template <ENUM_PARAMS(N, typename A)> \
+- void CAT(invoker, N)( \
+- ProcessBase* _, \
+- const std::tr1::function<void(ENUM_PARAMS(N, A))>& f, \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- f(ENUM_PARAMS(N, a)); \
+- } \
+- \
+- template <ENUM_PARAMS(N, typename A)> \
+- void CAT(dispatcher, N)( \
+- const UPID& pid, \
+- const std::tr1::function<void(ENUM_PARAMS(N, A))>& f, \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > invoker( \
+- new std::tr1::function<void(ProcessBase*)>( \
+- std::tr1::bind(&internal::CAT(invoker, N)<ENUM_PARAMS(N, A)>, \
+- std::tr1::placeholders::_1, \
+- f, \
+- ENUM_PARAMS(N, a)))); \
+- \
+- internal::dispatch(pid, invoker); \
+- }
+-
+- REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
+-#undef TEMPLATE
+-
+-
+- // We can't easily use 'std::tr1::_Placeholder<X>' when doing macro
+- // expansion via ENUM_BINARY_PARAMS because compilers don't like it
+- // when you try and concatenate '<' 'N' '>'. Thus, we typedef them.
+-#define TEMPLATE(Z, N, DATA) \
+- typedef std::tr1::_Placeholder<INC(N)> _ ## N;
+-
+- REPEAT(10, TEMPLATE, _)
+-#undef TEMPLATE
+-
+-} // namespace internal {
+-
+-
+-// Now we define defer calls for functions and bind statements.
+-inline Deferred<void(void)> defer(const std::tr1::function<void(void)>& f)
+-{
+- if (__process__ != NULL) {
+- // In C++11:
+- // const UPID pid = __process__->self();
+- // return []() {
+- // internal::dispatch(pid, [](ProcessBase* _) { f(); });
+- // }
+- return std::tr1::function<void(void)>(
+- std::tr1::bind(&internal::dispatcher,
+- __process__->self(),
+- f));
+- }
+-
+- return __executor__->defer(f);
+-}
+-
+-
+-#define TEMPLATE(Z, N, DATA) \
+- template <ENUM_PARAMS(N, typename A)> \
+- Deferred<void(ENUM_PARAMS(N, A))> defer( \
+- const std::tr1::function<void(ENUM_PARAMS(N, A))>& f) \
+- { \
+- if (__process__ != NULL) { \
+- return std::tr1::function<void(ENUM_PARAMS(N, A))>( \
+- std::tr1::bind(&internal::CAT(dispatcher, N)<ENUM_PARAMS(N, A)>, \
+- __process__->self(), \
+- f, \
+- ENUM_BINARY_PARAMS(N, internal::_, () INTERCEPT))); \
+- } \
+- \
+- return __executor__->defer(f); \
+- } \
+- \
+- template <typename R, ENUM_PARAMS(N, typename A)> \
+- Deferred<Future<R>(ENUM_PARAMS(N, A))> defer( \
+- const std::tr1::function<Future<R>(ENUM_PARAMS(N, A))>& f) \
+- { \
+- if (__process__ != NULL) { \
+- return std::tr1::function<Future<R>(ENUM_PARAMS(N, A))>( \
+- std::tr1::bind(&internal::CAT(dispatcher, N)<ENUM_PARAMS(N, A)>, \
+- __process__->self(), \
+- f, \
+- ENUM_BINARY_PARAMS(N, internal::_, () INTERCEPT))); \
+- } \
+- \
+- return __executor__->defer(f); \
+- }
+-
+- REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
+-#undef TEMPLATE
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_DEFER_HPP__
+diff --git a/3rdparty/libprocess/include/process/deferred.hpp b/3rdparty/libprocess/include/process/deferred.hpp
+deleted file mode 100644
+index 8907e80..0000000
+--- a/3rdparty/libprocess/include/process/deferred.hpp
++++ /dev/null
+@@ -1,136 +0,0 @@
+-#ifndef __PROCESS_DEFERRED_HPP__
+-#define __PROCESS_DEFERRED_HPP__
+-
+-#include <tr1/functional>
+-
+-#include <process/future.hpp>
+-#include <process/pid.hpp>
+-
+-#include <stout/preprocessor.hpp>
+-
+-namespace process {
+-
+-// Forward declarations (removing these produces cryptic compiler
+-// errors even though we are just using them to declare friends).
+-class Executor;
+-template <typename _F> struct _Defer;
+-
+-
+-// Acts like a function call but runs within an asynchronous execution
+-// context such as an Executor or a ProcessBase (enforced because only
+-// an executor or the 'defer' routines are allowed to create them).
+-template <typename F>
+-struct Deferred : std::tr1::function<F>
+-{
+-private:
+- // Only an Executor and the 'defer' routines can create these.
+- friend class Executor;
+-
+- template <typename _F> friend struct _Defer;
+-
+- friend Deferred<void(void)> defer(const std::tr1::function<void(void)>& f);
+-
+-#define TEMPLATE(Z, N, DATA) \
+- template <ENUM_PARAMS(N, typename A)> \
+- friend Deferred<void(ENUM_PARAMS(N, A))> defer( \
+- const std::tr1::function<void(ENUM_PARAMS(N, A))>& f);
+-
+- REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
+-#undef TEMPLATE
+-
+- Deferred(const std::tr1::function<F>& f) : std::tr1::function<F>(f) {}
+-};
+-
+-
+-// The result of invoking the 'defer' routines is actually an internal
+-// type, effectively just a wrapper around the result of invoking
+-// 'std::tr1::bind'. However, we want the result of bind to be
+-// castable to a 'Deferred' but we don't want anyone to be able to
+-// create a 'Deferred' so we use a level-of-indirection via this type.
+-template <typename F>
+-struct _Defer : std::tr1::_Bind<F>
+-{
+- template <typename _F>
+- operator Deferred<_F> ()
+- {
+- return Deferred<_F>(std::tr1::function<_F>(*this));
+- }
+-
+-private:
+- friend class Executor;
+-
+- template <typename T>
+- friend _Defer<void(*(PID<T>, void (T::*)(void)))
+- (const PID<T>&, void (T::*)(void))>
+- defer(const PID<T>& pid, void (T::*method)(void));
+-
+-#define TEMPLATE(Z, N, DATA) \
+- template <typename T, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- friend _Defer<void(*(PID<T>, \
+- void (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, A))) \
+- (const PID<T>&, \
+- void (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, P))> \
+- defer(const PID<T>& pid, \
+- void (T::*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a));
+-
+- REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
+-#undef TEMPLATE
+-
+- template <typename R, typename T>
+- friend _Defer<Future<R>(*(PID<T>, Future<R> (T::*)(void)))(
+- const PID<T>&, Future<R> (T::*)(void))>
+- defer(const PID<T>& pid, Future<R> (T::*method)(void));
+-
+-#define TEMPLATE(Z, N, DATA) \
+- template <typename R, \
+- typename T, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- friend _Defer<Future<R>(*(PID<T>, \
+- Future<R> (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, A))) \
+- (const PID<T>&, \
+- Future<R> (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, P))> \
+- defer(const PID<T>& pid, \
+- Future<R> (T::*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a));
+-
+- REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
+-#undef TEMPLATE
+-
+- template <typename R, typename T>
+- friend _Defer<Future<R>(*(PID<T>, R (T::*)(void)))(
+- const PID<T>&, R (T::*)(void))>
+- defer(const PID<T>& pid, R (T::*method)(void));
+-
+-#define TEMPLATE(Z, N, DATA) \
+- template <typename R, \
+- typename T, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- friend _Defer<Future<R>(*(PID<T>, \
+- R (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, A))) \
+- (const PID<T>&, \
+- R (T::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, P))> \
+- defer(const PID<T>& pid, \
+- R (T::*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a));
+-
+- REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
+-#undef TEMPLATE
+-
+- _Defer(const std::tr1::_Bind<F>& b)
+- : std::tr1::_Bind<F>(b) {}
+-};
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_DEFERRED_HPP__
+diff --git a/3rdparty/libprocess/include/process/delay.hpp b/3rdparty/libprocess/include/process/delay.hpp
+deleted file mode 100644
+index 97acd76..0000000
+--- a/3rdparty/libprocess/include/process/delay.hpp
++++ /dev/null
+@@ -1,119 +0,0 @@
+-#ifndef __PROCESS_DELAY_HPP__
+-#define __PROCESS_DELAY_HPP__
+-
+-#include <tr1/functional>
+-
+-#include <process/dispatch.hpp>
+-#include <process/timer.hpp>
+-
+-#include <stout/duration.hpp>
+-#include <stout/preprocessor.hpp>
+-
+-namespace process {
+-
+-// The 'delay' mechanism enables you to delay a dispatch to a process
+-// for some specified number of seconds. Returns a Timer instance that
+-// can be cancelled (but it might have already executed or be
+-// executing concurrently).
+-
+-template <typename T>
+-Timer delay(const Duration& duration,
+- const PID<T>& pid,
+- void (T::*method)())
+-{
+- std::tr1::shared_ptr<std::tr1::function<void(T*)> > thunk(
+- new std::tr1::function<void(T*)>(
+- std::tr1::bind(method, std::tr1::placeholders::_1)));
+-
+- std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > dispatcher(
+- new std::tr1::function<void(ProcessBase*)>(
+- std::tr1::bind(&internal::vdispatcher<T>,
+- std::tr1::placeholders::_1,
+- thunk)));
+-
+- std::tr1::function<void(void)> dispatch =
+- std::tr1::bind(internal::dispatch,
+- pid,
+- dispatcher,
+- internal::canonicalize(method));
+-
+- return Timer::create(duration, dispatch);
+-}
+-
+-
+-template <typename T>
+-Timer delay(const Duration& duration,
+- const Process<T>& process,
+- void (T::*method)())
+-{
+- return delay(duration, process.self(), method);
+-}
+-
+-
+-template <typename T>
+-Timer delay(const Duration& duration,
+- const Process<T>* process,
+- void (T::*method)())
+-{
+- return delay(duration, process->self(), method);
+-}
+-
+-
+-#define TEMPLATE(Z, N, DATA) \
+- template <typename T, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- Timer delay(const Duration& duration, \
+- const PID<T>& pid, \
+- void (T::*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- std::tr1::shared_ptr<std::tr1::function<void(T*)> > thunk( \
+- new std::tr1::function<void(T*)>( \
+- std::tr1::bind(method, \
+- std::tr1::placeholders::_1, \
+- ENUM_PARAMS(N, a)))); \
+- \
+- std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > dispatcher( \
+- new std::tr1::function<void(ProcessBase*)>( \
+- std::tr1::bind(&internal::vdispatcher<T>, \
+- std::tr1::placeholders::_1, \
+- thunk))); \
+- \
+- std::tr1::function<void(void)> dispatch = \
+- std::tr1::bind(internal::dispatch, \
+- pid, \
+- dispatcher, \
+- internal::canonicalize(method)); \
+- \
+- return Timer::create(duration, dispatch); \
+- } \
+- \
+- template <typename T, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- Timer delay(const Duration& duration, \
+- const Process<T>& process, \
+- void (T::*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- return delay(duration, process.self(), method, ENUM_PARAMS(N, a)); \
+- } \
+- \
+- template <typename T, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- Timer delay(const Duration& duration, \
+- const Process<T>* process, \
+- void (T::*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- return delay(duration, process->self(), method, ENUM_PARAMS(N, a)); \
+- }
+-
+- REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
+-#undef TEMPLATE
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_DELAY_HPP__
+diff --git a/3rdparty/libprocess/include/process/dispatch.hpp b/3rdparty/libprocess/include/process/dispatch.hpp
+deleted file mode 100644
+index b337a87..0000000
+--- a/3rdparty/libprocess/include/process/dispatch.hpp
++++ /dev/null
+@@ -1,478 +0,0 @@
+-#ifndef __PROCESS_DISPATCH_HPP__
+-#define __PROCESS_DISPATCH_HPP__
+-
+-#include <string>
+-
+-#include <tr1/functional>
+-#include <tr1/memory> // TODO(benh): Replace all shared_ptr with unique_ptr.
+-
+-#include <process/process.hpp>
+-
+-#include <stout/preprocessor.hpp>
+-
+-namespace process {
+-
+-// The dispatch mechanism enables you to "schedule" a method to get
+-// invoked on a process. The result of that method invocation is
+-// accessible via the future that is returned by the dispatch method
+-// (note, however, that it might not be the _same_ future as the one
+-// returned from the method, if the method even returns a future, see
+-// below). Assuming some class 'Fibonacci' has a (visible) method
+-// named 'compute' that takes an integer, N (and returns the Nth
+-// fibonacci number) you might use dispatch like so:
+-//
+-// PID<Fibonacci> pid = spawn(new Fibonacci(), true); // Use the GC.
+-// Future<int> f = dispatch(pid, &Fibonacci::compute, 10);
+-//
+-// Because the pid argument is "typed" we can ensure that methods are
+-// only invoked on processes that are actually of that type. Providing
+-// this mechanism for varying numbers of function types and arguments
+-// requires support for variadic templates, slated to be released in
+-// C++11. Until then, we use the Boost preprocessor macros to
+-// accomplish the same thing (all be it less cleanly). See below for
+-// those definitions.
+-//
+-// Dispatching is done via a level of indirection. The dispatch
+-// routine itself creates a promise that is passed as an argument to a
+-// partially applied 'dispatcher' function (defined below). The
+-// dispatcher routines get passed to the actual process via an
+-// internal routine called, not suprisingly, 'dispatch', defined
+-// below:
+-
+-namespace internal {
+-
+-// The internal dispatch routine schedules a function to get invoked
+-// within the context of the process associated with the specified pid
+-// (first argument), unless that process is no longer valid. Note that
+-// this routine does not expect anything in particular about the
+-// specified function (second argument). The semantics are simple: the
+-// function gets applied/invoked with the process as its first
+-// argument. Currently we wrap the function in a shared_ptr but this
+-// will probably change in the future to unique_ptr (or a variant).
+-void dispatch(
+- const UPID& pid,
+- const std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> >& f,
+- const std::string& method = std::string());
+-
+-// For each return type (void, future, value) there is a dispatcher
+-// function which should complete the picture. Given the process
+-// argument these routines downcast the process to the correct subtype
+-// and invoke the thunk using the subtype as the argument
+-// (receiver). Note that we must use dynamic_cast because we permit a
+-// process to use multiple inheritance (e.g., to expose multiple
+-// callback interfaces).
+-
+-template <typename T>
+-void vdispatcher(
+- ProcessBase* process,
+- std::tr1::shared_ptr<std::tr1::function<void(T*)> > thunk)
+-{
+- assert(process != NULL);
+- T* t = dynamic_cast<T*>(process);
+- assert(t != NULL);
+- (*thunk)(t);
+-}
+-
+-
+-template <typename R, typename T>
+-void pdispatcher(
+- ProcessBase* process,
+- std::tr1::shared_ptr<std::tr1::function<Future<R>(T*)> > thunk,
+- std::tr1::shared_ptr<Promise<R> > promise)
+-{
+- assert(process != NULL);
+- T* t = dynamic_cast<T*>(process);
+- assert(t != NULL);
+- promise->associate((*thunk)(t));
+-}
+-
+-
+-template <typename R, typename T>
+-void rdispatcher(
+- ProcessBase* process,
+- std::tr1::shared_ptr<std::tr1::function<R(T*)> > thunk,
+- std::tr1::shared_ptr<Promise<R> > promise)
+-{
+- assert(process != NULL);
+- T* t = dynamic_cast<T*>(process);
+- assert(t != NULL);
+- promise->set((*thunk)(t));
+-}
+-
+-
+-// Canonicalizes a pointer to a member function (i.e., method) into a
+-// bytes representation for comparison (e.g., in tests).
+-template <typename Method>
+-std::string canonicalize(Method method)
+-{
+- return std::string(reinterpret_cast<const char*>(&method), sizeof(method));
+-}
+-
+-} // namespace internal {
+-
+-
+-// Okay, now for the definition of the dispatch routines
+-// themselves. For each routine we provide the version in C++11 using
+-// variadic templates so the reader can see what the Boost
+-// preprocessor macros are effectively providing. Using C++11 closures
+-// would shorten these definitions even more.
+-//
+-// First, definitions of dispatch for methods returning void:
+-//
+-// template <typename T, typename ...P>
+-// void dispatch(
+-// const PID<T>& pid,
+-// void (T::*method)(P...),
+-// P... p)
+-// {
+-// std::tr1::shared_ptr<std::tr1::function<void(T*)> > thunk(
+-// new std::tr1::function<void(T*)>(
+-// std::tr1::bind(method,
+-// std::tr1::placeholders::_1,
+-// std::forward<P>(p)...)));
+-//
+-// std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > dispatcher(
+-// new std::tr1::function<void(ProcessBase*)>(
+-// std::tr1::bind(&internal::vdispatcher<T>,
+-// std::tr1::placeholders::_1,
+-// thunk)));
+-//
+-// internal::dispatch(pid, dispatcher, internal::canonicalize(method));
+-// }
+-
+-template <typename T>
+-void dispatch(
+- const PID<T>& pid,
+- void (T::*method)(void))
+-{
+- std::tr1::shared_ptr<std::tr1::function<void(T*)> > thunk(
+- new std::tr1::function<void(T*)>(
+- std::tr1::bind(method, std::tr1::placeholders::_1)));
+-
+- std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > dispatcher(
+- new std::tr1::function<void(ProcessBase*)>(
+- std::tr1::bind(&internal::vdispatcher<T>,
+- std::tr1::placeholders::_1,
+- thunk)));
+-
+- internal::dispatch(pid, dispatcher, internal::canonicalize(method));
+-}
+-
+-template <typename T>
+-void dispatch(
+- const Process<T>& process,
+- void (T::*method)(void))
+-{
+- dispatch(process.self(), method);
+-}
+-
+-template <typename T>
+-void dispatch(
+- const Process<T>* process,
+- void (T::*method)(void))
+-{
+- dispatch(process->self(), method);
+-}
+-
+-#define TEMPLATE(Z, N, DATA) \
+- template <typename T, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- void dispatch( \
+- const PID<T>& pid, \
+- void (T::*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- std::tr1::shared_ptr<std::tr1::function<void(T*)> > thunk( \
+- new std::tr1::function<void(T*)>( \
+- std::tr1::bind(method, \
+- std::tr1::placeholders::_1, \
+- ENUM_PARAMS(N, a)))); \
+- \
+- std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > dispatcher( \
+- new std::tr1::function<void(ProcessBase*)>( \
+- std::tr1::bind(&internal::vdispatcher<T>, \
+- std::tr1::placeholders::_1, \
+- thunk))); \
+- \
+- internal::dispatch(pid, dispatcher, internal::canonicalize(method)); \
+- } \
+- \
+- template <typename T, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- void dispatch( \
+- const Process<T>& process, \
+- void (T::*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- dispatch(process.self(), method, ENUM_PARAMS(N, a)); \
+- } \
+- \
+- template <typename T, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- void dispatch( \
+- const Process<T>* process, \
+- void (T::*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- dispatch(process->self(), method, ENUM_PARAMS(N, a)); \
+- }
+-
+- REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
+-#undef TEMPLATE
+-
+-
+-// Next, definitions of methods returning a future:
+-//
+-// template <typename R, typename T, typename ...P>
+-// Future<R> dispatch(
+-// const PID<T>& pid,
+-// Future<R> (T::*method)(P...),
+-// P... p)
+-// {
+-// std::tr1::shared_ptr<std::tr1::function<Future<R>(T*)> > thunk(
+-// new std::tr1::function<Future<R>(T*)>(
+-// std::tr1::bind(method,
+-// std::tr1::placeholders::_1,
+-// std::forward<P>(p)...)));
+-//
+-// std::tr1::shared_ptr<Promise<R> > promise(new Promise<R>());
+-// Future<R> future = promise->future();
+-//
+-// std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > dispatcher(
+-// new std::tr1::function<void(ProcessBase*)>(
+-// std::tr1::bind(&internal::pdispatcher<R, T>,
+-// std::tr1::placeholders::_1,
+-// thunk, promise)));
+-//
+-// internal::dispatch(pid, dispatcher, internal::canonicalize(method));
+-//
+-// return future;
+-// }
+-
+-template <typename R, typename T>
+-Future<R> dispatch(
+- const PID<T>& pid,
+- Future<R> (T::*method)(void))
+-{
+- std::tr1::shared_ptr<std::tr1::function<Future<R>(T*)> > thunk(
+- new std::tr1::function<Future<R>(T*)>(
+- std::tr1::bind(method, std::tr1::placeholders::_1)));
+-
+- std::tr1::shared_ptr<Promise<R> > promise(new Promise<R>());
+- Future<R> future = promise->future();
+-
+- std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > dispatcher(
+- new std::tr1::function<void(ProcessBase*)>(
+- std::tr1::bind(&internal::pdispatcher<R, T>,
+- std::tr1::placeholders::_1,
+- thunk, promise)));
+-
+- internal::dispatch(pid, dispatcher, internal::canonicalize(method));
+-
+- return future;
+-}
+-
+-template <typename R, typename T>
+-Future<R> dispatch(
+- const Process<T>& process,
+- Future<R> (T::*method)(void))
+-{
+- return dispatch(process.self(), method);
+-}
+-
+-template <typename R, typename T>
+-Future<R> dispatch(
+- const Process<T>* process,
+- Future<R> (T::*method)(void))
+-{
+- return dispatch(process->self(), method);
+-}
+-
+-#define TEMPLATE(Z, N, DATA) \
+- template <typename R, \
+- typename T, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- Future<R> dispatch( \
+- const PID<T>& pid, \
+- Future<R> (T::*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- std::tr1::shared_ptr<std::tr1::function<Future<R>(T*)> > thunk( \
+- new std::tr1::function<Future<R>(T*)>( \
+- std::tr1::bind(method, \
+- std::tr1::placeholders::_1, \
+- ENUM_PARAMS(N, a)))); \
+- \
+- std::tr1::shared_ptr<Promise<R> > promise(new Promise<R>()); \
+- Future<R> future = promise->future(); \
+- \
+- std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > dispatcher( \
+- new std::tr1::function<void(ProcessBase*)>( \
+- std::tr1::bind(&internal::pdispatcher<R, T>, \
+- std::tr1::placeholders::_1, \
+- thunk, promise))); \
+- \
+- internal::dispatch(pid, dispatcher, internal::canonicalize(method)); \
+- \
+- return future; \
+- } \
+- \
+- template <typename R, \
+- typename T, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- Future<R> dispatch( \
+- const Process<T>& process, \
+- Future<R> (T::*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- return dispatch(process.self(), method, ENUM_PARAMS(N, a)); \
+- } \
+- \
+- template <typename R, \
+- typename T, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- Future<R> dispatch( \
+- const Process<T>* process, \
+- Future<R> (T::*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- return dispatch(process->self(), method, ENUM_PARAMS(N, a)); \
+- }
+-
+- REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
+-#undef TEMPLATE
+-
+-
+-// Next, definitions of methods returning a value.
+-//
+-// template <typename R, typename T, typename ...P>
+-// Future<R> dispatch(
+-// const PID<T>& pid,
+-// R (T::*method)(P...),
+-// P... p)
+-// {
+-// std::tr1::shared_ptr<std::tr1::function<R(T*)> > thunk(
+-// new std::tr1::function<R(T*)>(
+-// std::tr1::bind(method,
+-// std::tr1::placeholders::_1,
+-// std::forward<P>(p)...)));
+-//
+-// std::tr1::shared_ptr<Promise<R> > promise(new Promise<R>());
+-// Future<R> future = promise->future();
+-//
+-// std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > dispatcher(
+-// new std::tr1::function<void(ProcessBase*)>(
+-// std::tr1::bind(&internal::rdispatcher<R, T>,
+-// std::tr1::placeholders::_1,
+-// thunk, promise)));
+-//
+-// internal::dispatch(pid, dispatcher, internal::canonicalize(method));
+-//
+-// return future;
+-// }
+-
+-template <typename R, typename T>
+-Future<R> dispatch(
+- const PID<T>& pid,
+- R (T::*method)(void))
+-{
+- std::tr1::shared_ptr<std::tr1::function<R(T*)> > thunk(
+- new std::tr1::function<R(T*)>(
+- std::tr1::bind(method, std::tr1::placeholders::_1)));
+-
+- std::tr1::shared_ptr<Promise<R> > promise(new Promise<R>());
+- Future<R> future = promise->future();
+-
+- std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > dispatcher(
+- new std::tr1::function<void(ProcessBase*)>(
+- std::tr1::bind(&internal::rdispatcher<R, T>,
+- std::tr1::placeholders::_1,
+- thunk, promise)));
+-
+- internal::dispatch(pid, dispatcher, internal::canonicalize(method));
+-
+- return future;
+-}
+-
+-template <typename R, typename T>
+-Future<R> dispatch(
+- const Process<T>& process,
+- R (T::*method)(void))
+-{
+- return dispatch(process.self(), method);
+-}
+-
+-template <typename R, typename T>
+-Future<R> dispatch(
+- const Process<T>* process,
+- R (T::*method)(void))
+-{
+- return dispatch(process->self(), method);
+-}
+-
+-#define TEMPLATE(Z, N, DATA) \
+- template <typename R, \
+- typename T, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- Future<R> dispatch( \
+- const PID<T>& pid, \
+- R (T::*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- std::tr1::shared_ptr<std::tr1::function<R(T*)> > thunk( \
+- new std::tr1::function<R(T*)>( \
+- std::tr1::bind(method, \
+- std::tr1::placeholders::_1, \
+- ENUM_PARAMS(N, a)))); \
+- \
+- std::tr1::shared_ptr<Promise<R> > promise(new Promise<R>()); \
+- Future<R> future = promise->future(); \
+- \
+- std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > dispatcher( \
+- new std::tr1::function<void(ProcessBase*)>( \
+- std::tr1::bind(&internal::rdispatcher<R, T>, \
+- std::tr1::placeholders::_1, \
+- thunk, promise))); \
+- \
+- internal::dispatch(pid, dispatcher, internal::canonicalize(method)); \
+- \
+- return future; \
+- } \
+- \
+- template <typename R, \
+- typename T, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- Future<R> dispatch( \
+- const Process<T>& process, \
+- R (T::*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- return dispatch(process.self(), method, ENUM_PARAMS(N, a)); \
+- } \
+- \
+- template <typename R, \
+- typename T, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- Future<R> dispatch( \
+- const Process<T>* process, \
+- R (T::*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- return dispatch(process->self(), method, ENUM_PARAMS(N, a)); \
+- }
+-
+- REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
+-#undef TEMPLATE
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_DISPATCH_HPP__
+diff --git a/3rdparty/libprocess/include/process/event.hpp b/3rdparty/libprocess/include/process/event.hpp
+deleted file mode 100644
+index cf728da..0000000
+--- a/3rdparty/libprocess/include/process/event.hpp
++++ /dev/null
+@@ -1,199 +0,0 @@
+-#ifndef __PROCESS_EVENT_HPP__
+-#define __PROCESS_EVENT_HPP__
+-
+-#include <tr1/functional>
+-#include <tr1/memory> // TODO(benh): Replace all shared_ptr with unique_ptr.
+-
+-#include <process/future.hpp>
+-#include <process/http.hpp>
+-#include <process/message.hpp>
+-#include <process/socket.hpp>
+-
+-namespace process {
+-
+-// Forward declarations.
+-class ProcessBase;
+-struct MessageEvent;
+-struct DispatchEvent;
+-struct HttpEvent;
+-struct ExitedEvent;
+-struct TerminateEvent;
+-
+-
+-struct EventVisitor
+-{
+- virtual ~EventVisitor() {}
+- virtual void visit(const MessageEvent& event) {}
+- virtual void visit(const DispatchEvent& event) {}
+- virtual void visit(const HttpEvent& event) {}
+- virtual void visit(const ExitedEvent& event) {}
+- virtual void visit(const TerminateEvent& event) {}
+-};
+-
+-
+-struct Event
+-{
+- virtual ~Event() {}
+-
+- virtual void visit(EventVisitor* visitor) const = 0;
+-
+- template <typename T>
+- bool is() const
+- {
+- bool result = false;
+- struct IsVisitor : EventVisitor
+- {
+- IsVisitor(bool* _result) : result(_result) {}
+- virtual void visit(const T& t) { *result = true; }
+- bool* result;
+- } visitor(&result);
+- visit(&visitor);
+- return result;
+- }
+-
+- template <typename T>
+- const T& as() const
+- {
+- const T* result = NULL;
+- struct AsVisitor : EventVisitor
+- {
+- AsVisitor(const T** _result) : result(_result) {}
+- virtual void visit(const T& t) { *result = &t; }
+- const T** result;
+- } visitor(&result);
+- visit(&visitor);
+- if (result == NULL) {
+- std::cerr << "Attempting to \"cast\" event incorrectly!" << std::endl;
+- abort();
+- }
+- return *result;
+- }
+-};
+-
+-
+-struct MessageEvent : Event
+-{
+- MessageEvent(Message* _message)
+- : message(_message) {}
+-
+- virtual ~MessageEvent()
+- {
+- delete message;
+- }
+-
+- virtual void visit(EventVisitor* visitor) const
+- {
+- visitor->visit(*this);
+- }
+-
+- Message* const message;
+-
+-private:
+- // Not copyable, not assignable.
+- MessageEvent(const MessageEvent&);
+- MessageEvent& operator = (const MessageEvent&);
+-};
+-
+-
+-struct HttpEvent : Event
+-{
+- HttpEvent(const Socket& _socket, http::Request* _request)
+- : socket(_socket), request(_request) {}
+-
+- virtual ~HttpEvent()
+- {
+- delete request;
+- }
+-
+- virtual void visit(EventVisitor* visitor) const
+- {
+- visitor->visit(*this);
+- }
+-
+- const Socket socket;
+- http::Request* const request;
+-
+-private:
+- // Not copyable, not assignable.
+- HttpEvent(const HttpEvent&);
+- HttpEvent& operator = (const HttpEvent&);
+-};
+-
+-
+-struct DispatchEvent : Event
+-{
+- DispatchEvent(
+- const UPID& _pid,
+- const std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> >& _f,
+- const std::string& _method)
+- : pid(_pid),
+- f(_f),
+- method(_method)
+- {}
+-
+- virtual void visit(EventVisitor* visitor) const
+- {
+- visitor->visit(*this);
+- }
+-
+- // PID receiving the dispatch.
+- const UPID pid;
+-
+- // Function to get invoked as a result of this dispatch event.
+- const std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > f;
+-
+- // Canonical "byte" representation of a pointer to a member function
+- // (i.e., method) encapsulated in the above function (or empty if
+- // not applicable). Note that we use a byte representation because a
+- // pointer to a member function is not actually a pointer, but
+- // instead a POD.
+- // TODO(benh): Perform canonicalization lazily.
+- const std::string method;
+-
+-private:
+- // Not copyable, not assignable.
+- DispatchEvent(const DispatchEvent&);
+- DispatchEvent& operator = (const DispatchEvent&);
+-};
+-
+-
+-struct ExitedEvent : Event
+-{
+- ExitedEvent(const UPID& _pid)
+- : pid(_pid) {}
+-
+- virtual void visit(EventVisitor* visitor) const
+- {
+- visitor->visit(*this);
+- }
+-
+- const UPID pid;
+-
+-private:
+- // Not copyable, not assignable.
+- ExitedEvent(const ExitedEvent&);
+- ExitedEvent& operator = (const ExitedEvent&);
+-};
+-
+-
+-struct TerminateEvent : Event
+-{
+- TerminateEvent(const UPID& _from)
+- : from(_from) {}
+-
+- virtual void visit(EventVisitor* visitor) const
+- {
+- visitor->visit(*this);
+- }
+-
+- const UPID from;
+-
+-private:
+- // Not copyable, not assignable.
+- TerminateEvent(const TerminateEvent&);
+- TerminateEvent& operator = (const TerminateEvent&);
+-};
+-
+-} // namespace event {
+-
+-#endif // __PROCESS_EVENT_HPP__
+diff --git a/3rdparty/libprocess/include/process/executor.hpp b/3rdparty/libprocess/include/process/executor.hpp
+deleted file mode 100644
+index f203476..0000000
+--- a/3rdparty/libprocess/include/process/executor.hpp
++++ /dev/null
+@@ -1,260 +0,0 @@
+-#ifndef __PROCESS_EXECUTOR_HPP__
+-#define __PROCESS_EXECUTOR_HPP__
+-
+-#include <process/deferred.hpp>
+-#include <process/dispatch.hpp>
+-#include <process/id.hpp>
+-
+-#include <stout/preprocessor.hpp>
+-#include <stout/thread.hpp>
+-
+-namespace process {
+-
+-// Underlying "process" which handles invoking actual callbacks
+-// created through an Executor.
+-class ExecutorProcess : public Process<ExecutorProcess>
+-{
+-private:
+- friend class Executor;
+-
+- ExecutorProcess() : ProcessBase(ID::generate("__executor__")) {}
+- virtual ~ExecutorProcess() {}
+-
+- // Not copyable, not assignable.
+- ExecutorProcess(const ExecutorProcess&);
+- ExecutorProcess& operator = (const ExecutorProcess&);
+-
+- // No arg invoke.
+- void invoke(const std::tr1::function<void(void)>& f) { f(); }
+-
+- // Args invoke.
+-#define TEMPLATE(Z, N, DATA) \
+- template <ENUM_PARAMS(N, typename A)> \
+- void CAT(invoke, N)( \
+- const std::tr1::function<void(ENUM_PARAMS(N, A))>& f, \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- f(ENUM_PARAMS(N, a)); \
+- }
+-
+- REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
+-#undef TEMPLATE
+-};
+-
+-
+-// Provides an abstraction that can take a standard function object
+-// and convert it to a 'Deferred'. Each converted function object will
+-// get invoked serially with respect to one another.
+-class Executor
+-{
+-public:
+- Executor()
+- {
+- spawn(process);
+- }
+-
+- ~Executor()
+- {
+- terminate(process);
+- wait(process);
+- }
+-
+- void stop()
+- {
+- terminate(process);
+-
+- // TODO(benh): Note that this doesn't wait because that could
+- // cause a deadlock ... thus, the semantics here are that no more
+- // dispatches will occur after this function returns but one may
+- // be occuring concurrently.
+- }
+-
+- // We can't easily use 'std::tr1::_Placeholder<X>' when doing macro
+- // expansion via ENUM_BINARY_PARAMS because compilers don't like it
+- // when you try and concatenate '<' 'N' '>'. Thus, we typedef them.
+-private:
+-#define TEMPLATE(Z, N, DATA) \
+- typedef std::tr1::_Placeholder<INC(N)> _ ## N;
+-
+- REPEAT(10, TEMPLATE, _)
+-#undef TEMPLATE
+-
+-public:
+- // We provide wrappers for all standard function objects.
+- Deferred<void(void)> defer(
+- const std::tr1::function<void(void)>& f)
+- {
+- return Deferred<void(void)>(
+- std::tr1::bind(
+- &Executor::dispatcher,
+- process.self(), f));
+- }
+-
+-#define TEMPLATE(Z, N, DATA) \
+- template <ENUM_PARAMS(N, typename A)> \
+- Deferred<void(ENUM_PARAMS(N, A))> defer( \
+- const std::tr1::function<void(ENUM_PARAMS(N, A))>& f) \
+- { \
+- return Deferred<void(ENUM_PARAMS(N, A))>( \
+- std::tr1::bind( \
+- &Executor::CAT(dispatcher, N)<ENUM_PARAMS(N, A)>, \
+- process.self(), f, \
+- ENUM_BINARY_PARAMS(N, _, () INTERCEPT))); \
+- }
+-
+- REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
+-#undef TEMPLATE
+-
+- // Unfortunately, it is currently difficult to "forward" type
+- // information from one result to another, so we must explicilty
+- // define wrappers for all std::tr1::bind results. First we start
+- // with the non-member std::tr1::bind results.
+- Deferred<void(void)> defer(
+- const std::tr1::_Bind<void(*(void))(void)>& b)
+- {
+- return defer(std::tr1::function<void(void)>(b));
+- }
+-
+-#define TEMPLATE(Z, N, DATA) \
+- template <ENUM_PARAMS(N, typename A)> \
+- Deferred<void(ENUM_PARAMS(N, A))> defer( \
+- const std::tr1::_Bind< \
+- void(*(ENUM_PARAMS(N, _))) \
+- (ENUM_PARAMS(N, A))>& b) \
+- { \
+- return defer(std::tr1::function<void(ENUM_PARAMS(N, A))>(b)); \
+- }
+-
+- REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
+-#undef TEMPLATE
+-
+- // Now the member std::tr1::bind results:
+- // 1. Non-const member (function), non-const pointer (receiver).
+- // 2. Const member, non-const pointer.
+- // 3. Const member, const pointer.
+- // 4. Non-const member, non-const reference.
+- // 5. Const member, non-const reference.
+- // 6. Const member, const reference.
+- // 7. Non-const member, value.
+- // 8. Const member, value.
+-#define TEMPLATE(Z, N, DATA) \
+- template <typename T ENUM_TRAILING_PARAMS(N, typename A)> \
+- Deferred<void(ENUM_PARAMS(N, A))> defer( \
+- const std::tr1::_Bind<std::tr1::_Mem_fn< \
+- void(T::*)(ENUM_PARAMS(N, A))> \
+- (T* ENUM_TRAILING_PARAMS(N, _))>& b) \
+- { \
+- return defer(std::tr1::function<void(ENUM_PARAMS(N, A))>(b)); \
+- } \
+- \
+- template <typename T ENUM_TRAILING_PARAMS(N, typename A)> \
+- Deferred<void(ENUM_PARAMS(N, A))> defer( \
+- const std::tr1::_Bind<std::tr1::_Mem_fn< \
+- void(T::*)(ENUM_PARAMS(N, A)) const> \
+- (T* ENUM_TRAILING_PARAMS(N, _))>& b) \
+- { \
+- return defer(std::tr1::function<void(ENUM_PARAMS(N, A))>(b)); \
+- } \
+- \
+- template <typename T ENUM_TRAILING_PARAMS(N, typename A)> \
+- Deferred<void(ENUM_PARAMS(N, A))> defer( \
+- const std::tr1::_Bind<std::tr1::_Mem_fn< \
+- void(T::*)(ENUM_PARAMS(N, A)) const> \
+- (const T* ENUM_TRAILING_PARAMS(N, _))>& b) \
+- { \
+- return defer(std::tr1::function<void(ENUM_PARAMS(N, A))>(b)); \
+- } \
+- \
+- template <typename T ENUM_TRAILING_PARAMS(N, typename A)> \
+- Deferred<void(ENUM_PARAMS(N, A))> defer( \
+- const std::tr1::_Bind<std::tr1::_Mem_fn< \
+- void(T::*)(ENUM_PARAMS(N, A))> \
+- (std::tr1::reference_wrapper<T> ENUM_TRAILING_PARAMS(N, _))>& b) \
+- { \
+- return defer(std::tr1::function<void(ENUM_PARAMS(N, A))>(b)); \
+- } \
+- \
+- template <typename T ENUM_TRAILING_PARAMS(N, typename A)> \
+- Deferred<void(ENUM_PARAMS(N, A))> defer( \
+- const std::tr1::_Bind<std::tr1::_Mem_fn< \
+- void(T::*)(ENUM_PARAMS(N, A)) const> \
+- (std::tr1::reference_wrapper<T> ENUM_TRAILING_PARAMS(N, _))>& b) \
+- { \
+- return defer(std::tr1::function<void(ENUM_PARAMS(N, A))>(b)); \
+- } \
+- \
+- template <typename T ENUM_TRAILING_PARAMS(N, typename A)> \
+- Deferred<void(ENUM_PARAMS(N, A))> defer( \
+- const std::tr1::_Bind<std::tr1::_Mem_fn< \
+- void(T::*)(ENUM_PARAMS(N, A)) const> \
+- (std::tr1::reference_wrapper<const T> ENUM_TRAILING_PARAMS(N, _))>& b) \
+- { \
+- return defer(std::tr1::function<void(ENUM_PARAMS(N, A))>(b)); \
+- } \
+- \
+- template <typename T ENUM_TRAILING_PARAMS(N, typename A)> \
+- Deferred<void(ENUM_PARAMS(N, A))> defer( \
+- const std::tr1::_Bind<std::tr1::_Mem_fn< \
+- void(T::*)(ENUM_PARAMS(N, A))> \
+- (T ENUM_TRAILING_PARAMS(N, _))>& b) \
+- { \
+- return defer(std::tr1::function<void(ENUM_PARAMS(N, A))>(b)); \
+- } \
+- \
+- template <typename T ENUM_TRAILING_PARAMS(N, typename A)> \
+- Deferred<void(ENUM_PARAMS(N, A))> defer( \
+- const std::tr1::_Bind<std::tr1::_Mem_fn< \
+- void(T::*)(ENUM_PARAMS(N, A)) const> \
+- (T ENUM_TRAILING_PARAMS(N, _))>& b) \
+- { \
+- return defer(std::tr1::function<void(ENUM_PARAMS(N, A))>(b)); \
+- }
+-
+- REPEAT(11, TEMPLATE, _) // No args and args A0 -> A9.
+-#undef TEMPLATE
+-
+-private:
+- // Not copyable, not assignable.
+- Executor(const Executor&);
+- Executor& operator = (const Executor&);
+-
+- static void dispatcher(
+- const PID<ExecutorProcess>& pid,
+- const std::tr1::function<void(void)>& f)
+- {
+- // TODO(benh): Why not just use internal::dispatch?
+- dispatch(pid, &ExecutorProcess::invoke, f);
+- }
+-
+-#define TEMPLATE(Z, N, DATA) \
+- template <ENUM_PARAMS(N, typename A)> \
+- static void CAT(dispatcher, N)( \
+- const PID<ExecutorProcess>& pid, \
+- const std::tr1::function<void(ENUM_PARAMS(N, A))>& f, \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- dispatch( \
+- pid, \
+- &ExecutorProcess::CAT(invoke, N)<ENUM_PARAMS(N, A)>, \
+- f, ENUM_PARAMS(N, a)); \
+- }
+-
+- REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
+-#undef TEMPLATE
+-
+- ExecutorProcess process;
+-};
+-
+-
+-// Per thread executor pointer. The extra level of indirection from
+-// _executor_ to __executor__ is used in order to take advantage of
+-// the ThreadLocal operators without needing the extra dereference as
+-// well as lazily construct the actual executor.
+-extern ThreadLocal<Executor>* _executor_;
+-
+-#define __executor__ \
+- (*_executor_ == NULL ? *_executor_ = new Executor() : *_executor_)
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_EXECUTOR_HPP__
+diff --git a/3rdparty/libprocess/include/process/filter.hpp b/3rdparty/libprocess/include/process/filter.hpp
+deleted file mode 100644
+index aa0c91b..0000000
+--- a/3rdparty/libprocess/include/process/filter.hpp
++++ /dev/null
+@@ -1,24 +0,0 @@
+-#ifndef __PROCESS_FILTER_HPP__
+-#define __PROCESS_FILTER_HPP__
+-
+-#include <process/event.hpp>
+-
+-namespace process {
+-
+-class Filter {
+-public:
+- virtual ~Filter() {}
+- virtual bool filter(const MessageEvent& event) { return false; }
+- virtual bool filter(const DispatchEvent& event) { return false; }
+- virtual bool filter(const HttpEvent& event) { return false; }
+- virtual bool filter(const ExitedEvent& event) { return false; }
+-};
+-
+-
+-// Use the specified filter on messages that get enqueued (note,
+-// however, that you cannot filter timeout messages).
+-void filter(Filter* filter);
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_FILTER_HPP__
+diff --git a/3rdparty/libprocess/include/process/future.hpp b/3rdparty/libprocess/include/process/future.hpp
+deleted file mode 100644
+index e473b3d..0000000
+--- a/3rdparty/libprocess/include/process/future.hpp
++++ /dev/null
+@@ -1,1023 +0,0 @@
+-#ifndef __PROCESS_FUTURE_HPP__
+-#define __PROCESS_FUTURE_HPP__
+-
+-#include <assert.h>
+-#include <stdlib.h> // For abort.
+-
+-#include <iostream>
+-#include <list>
+-#include <queue>
+-#include <set>
+-
+-#include <glog/logging.h>
+-
+-#include <tr1/functional>
+-#include <tr1/memory> // TODO(benh): Replace shared_ptr with unique_ptr.
+-
+-#include <process/latch.hpp>
+-#include <process/pid.hpp>
+-
+-#include <stout/duration.hpp>
+-#include <stout/error.hpp>
+-#include <stout/option.hpp>
+-#include <stout/preprocessor.hpp>
+-
+-namespace process {
+-
+-// Forward declaration (instead of include to break circular dependency).
+-template <typename _F> struct _Defer;
+-
+-namespace internal {
+-
+-template <typename T>
+-struct wrap;
+-
+-template <typename T>
+-struct unwrap;
+-
+-} // namespace internal {
+-
+-
+-// Forward declaration of Promise.
+-template <typename T>
+-class Promise;
+-
+-
+-// Definition of a "shared" future. A future can hold any
+-// copy-constructible value. A future is considered "shared" because
+-// by default a future can be accessed concurrently.
+-template <typename T>
+-class Future
+-{
+-public:
+- // Constructs a failed future.
+- static Future<T> failed(const std::string& message);
+-
+- Future();
+- Future(const T& _t);
+- Future(const Future<T>& that);
+- ~Future();
+-
+- // Futures are assignable (and copyable). This results in the
+- // reference to the previous future data being decremented and a
+- // reference to 'that' being incremented.
+- Future<T>& operator = (const Future<T>& that);
+-
+- // Comparision operators useful for using futures in collections.
+- bool operator == (const Future<T>& that) const;
+- bool operator < (const Future<T>& that) const;
+-
+- // Helpers to get the current state of this future.
+- bool isPending() const;
+- bool isReady() const;
+- bool isDiscarded() const;
+- bool isFailed() const;
+-
+- // Discards this future. This is similar to cancelling a future,
+- // however it also occurs when the last reference to this future
+- // gets cleaned up. Returns false if the future could not be
+- // discarded (for example, because it is ready or failed).
+- bool discard();
+-
+- // Waits for this future to become ready, discarded, or failed.
+- bool await(const Duration& duration = Seconds(-1)) const;
+-
+- // Return the value associated with this future, waits indefinitely
+- // until a value gets associated or until the future is discarded.
+- T get() const;
+-
+- // Returns the failure message associated with this future.
+- std::string failure() const;
+-
+- // Type of the callback functions that can get invoked when the
+- // future gets set, fails, or is discarded.
+- typedef std::tr1::function<void(const T&)> ReadyCallback;
+- typedef std::tr1::function<void(const std::string&)> FailedCallback;
+- typedef std::tr1::function<void(void)> DiscardedCallback;
+- typedef std::tr1::function<void(const Future<T>&)> AnyCallback;
+-
+- // Installs callbacks for the specified events and returns a const
+- // reference to 'this' in order to easily support chaining.
+- const Future<T>& onReady(const ReadyCallback& callback) const;
+- const Future<T>& onFailed(const FailedCallback& callback) const;
+- const Future<T>& onDiscarded(const DiscardedCallback& callback) const;
+- const Future<T>& onAny(const AnyCallback& callback) const;
+-
+- // Installs callbacks that get executed when this future is ready
+- // and associates the result of the callback with the future that is
+- // returned to the caller (which may be of a different type).
+- template <typename X>
+- Future<X> then(const std::tr1::function<Future<X>(const T&)>& f) const;
+-
+- template <typename X>
+- Future<X> then(const std::tr1::function<X(const T&)>& f) const;
+-
+- // Helpers for the compiler to be able to forward std::tr1::bind results.
+- template <typename X>
+- Future<X> then(const std::tr1::_Bind<X(*(void))(void)>& b) const
+- {
+- return then(std::tr1::function<X(const T&)>(b));
+- }
+-
+-#define TEMPLATE(Z, N, DATA) \
+- template <typename X, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- Future<X> then( \
+- const std::tr1::_Bind<X(*(ENUM_PARAMS(N, A))) \
+- (ENUM_PARAMS(N, P))>& b) const \
+- { \
+- return then(std::tr1::function<X(const T&)>(b)); \
+- }
+-
+- REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
+-#undef TEMPLATE
+-
+- template <typename X>
+- Future<X> then(const std::tr1::_Bind<Future<X>(*(void))(void)>& b) const
+- {
+- return then(std::tr1::function<Future<X>(const T&)>(b));
+- }
+-
+-#define TEMPLATE(Z, N, DATA) \
+- template <typename X, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- Future<X> then( \
+- const std::tr1::_Bind<Future<X>(*(ENUM_PARAMS(N, A))) \
+- (ENUM_PARAMS(N, P))>& b) const \
+- { \
+- return then(std::tr1::function<Future<X>(const T&)>(b)); \
+- }
+-
+- REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
+-#undef TEMPLATE
+-
+- // Helpers for the compiler to be able to forward 'defer' results.
+- template <typename X, typename U>
+- Future<X> then(const _Defer<Future<X>(*(PID<U>, X(U::*)(void)))
+- (const PID<U>&, X(U::*)(void))>& d) const
+- {
+- return then(std::tr1::function<Future<X>(const T&)>(d));
+- }
+-
+-#define TEMPLATE(Z, N, DATA) \
+- template <typename X, \
+- typename U, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- Future<X> then( \
+- const _Defer<Future<X>(*(PID<U>, \
+- X(U::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, A))) \
+- (const PID<U>&, \
+- X(U::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, P))>& d) const \
+- { \
+- return then(std::tr1::function<Future<X>(const T&)>(d)); \
+- }
+-
+- REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
+-#undef TEMPLATE
+-
+- template <typename X, typename U>
+- Future<X> then(const _Defer<Future<X>(*(PID<U>, Future<X>(U::*)(void)))
+- (const PID<U>&, Future<X>(U::*)(void))>& d) const
+- {
+- return then(std::tr1::function<Future<X>(const T&)>(d));
+- }
+-
+-#define TEMPLATE(Z, N, DATA) \
+- template <typename X, \
+- typename U, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- Future<X> then( \
+- const _Defer<Future<X>(*(PID<U>, \
+- Future<X>(U::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, A))) \
+- (const PID<U>&, \
+- Future<X>(U::*)(ENUM_PARAMS(N, P)), \
+- ENUM_PARAMS(N, P))>& d) const \
+- { \
+- return then(std::tr1::function<Future<X>(const T&)>(d)); \
+- }
+-
+- REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
+-#undef TEMPLATE
+-
+- // C++11 implementation (covers all functors).
+-#if __cplusplus >= 201103L
+- template <typename F>
+- auto then(F f) const
+- -> typename internal::wrap<decltype(f(T()))>::Type;
+-#endif
+-
+-private:
+- friend class Promise<T>;
+-
+- // Sets the value for this future, unless the future is already set,
+- // failed, or discarded, in which case it returns false.
+- bool set(const T& _t);
+-
+- // Sets this future as failed, unless the future is already set,
+- // failed, or discarded, in which case it returns false.
+- bool fail(const std::string& _message);
+-
+- void copy(const Future<T>& that);
+- void cleanup();
+-
+- enum State
+- {
+- PENDING,
+- READY,
+- FAILED,
+- DISCARDED,
+- };
+-
+- struct Data
+- {
+- Data();
+- ~Data();
+-
+- int lock;
+- Latch* latch;
+- State state;
+- T* t;
+- std::string* message; // Message associated with failure.
+- std::queue<ReadyCallback> onReadyCallbacks;
+- std::queue<FailedCallback> onFailedCallbacks;
+- std::queue<DiscardedCallback> onDiscardedCallbacks;
+- std::queue<AnyCallback> onAnyCallbacks;
+- };
+-
+- std::tr1::shared_ptr<Data> data;
+-};
+-
+-
+-// Helper for creating failed futures.
+-struct _Failure
+-{
+- _Failure(const std::string& _message) : message(_message) {}
+-
+- template <typename T>
+- operator Future<T> () const
+- {
+- return Future<T>::failed(message);
+- }
+-
+- const std::string message;
+-};
+-
+-
+-inline _Failure Failure(const std::string& message)
+-{
+- return _Failure(message);
+-}
+-
+-
+-inline _Failure Failure(const Error& error)
+-{
+- return _Failure(error.message);
+-}
+-
+-
+-// TODO(benh): Make Promise a subclass of Future?
+-template <typename T>
+-class Promise
+-{
+-public:
+- Promise();
+- Promise(const T& t);
+- virtual ~Promise();
+-
+- bool set(const T& _t);
+- bool set(const Future<T>& future); // Alias for associate.
+- bool associate(const Future<T>& future);
+- bool fail(const std::string& message);
+-
+- // Returns a copy of the future associated with this promise.
+- Future<T> future() const;
+-
+-private:
+- // Not copyable, not assignable.
+- Promise(const Promise<T>&);
+- Promise<T>& operator = (const Promise<T>&);
+-
+- Future<T> f;
+-};
+-
+-
+-template <>
+-class Promise<void>;
+-
+-
+-template <typename T>
+-class Promise<T&>;
+-
+-
+-template <typename T>
+-Promise<T>::Promise() {}
+-
+-
+-template <typename T>
+-Promise<T>::Promise(const T& t)
+- : f(t) {}
+-
+-
+-template <typename T>
+-Promise<T>::~Promise() {}
+-
+-
+-template <typename T>
+-bool Promise<T>::set(const T& t)
+-{
+- return f.set(t);
+-}
+-
+-
+-template <typename T>
+-bool Promise<T>::set(const Future<T>& future)
+-{
+- return associate(future);
+-}
+-
+-
+-template <typename T>
+-bool Promise<T>::associate(const Future<T>& future)
+-{
+- // TODO(jieyu): Make 'f' a true alias of 'future'. Currently, only
+- // 'discard' is associated in both directions. In other words, if a
+- // future gets discarded, the other future will also get discarded.
+- // For 'set' and 'fail', they are associated only in one direction.
+- // In other words, calling 'set' or 'fail' on this promise will not
+- // affect the result of the future that we associated.
+- f.onDiscarded(std::tr1::bind(&Future<T>::discard, future));
+-
+- if (!f.isPending()) {
+- return false;
+- }
+-
+- future
+- .onReady(std::tr1::bind(&Future<T>::set, f, std::tr1::placeholders::_1))
+- .onFailed(std::tr1::bind(&Future<T>::fail, f, std::tr1::placeholders::_1))
+- .onDiscarded(std::tr1::bind(&Future<T>::discard, f));
+-
+- return true;
+-}
+-
+-
+-template <typename T>
+-bool Promise<T>::fail(const std::string& message)
+-{
+- return f.fail(message);
+-}
+-
+-
+-template <typename T>
+-Future<T> Promise<T>::future() const
+-{
+- return f;
+-}
+-
+-
+-// Internal helper utilities.
+-namespace internal {
+-
+-template <typename T>
+-struct wrap
+-{
+- typedef Future<T> Type;
+-};
+-
+-
+-template <typename X>
+-struct wrap<Future<X> >
+-{
+- typedef Future<X> Type;
+-};
+-
+-
+-template <typename T>
+-struct unwrap
+-{
+- typedef T Type;
+-};
+-
+-
+-template <typename X>
+-struct unwrap<Future<X> >
+-{
+- typedef X Type;
+-};
+-
+-
+-inline void acquire(int* lock)
+-{
+- while (!__sync_bool_compare_and_swap(lock, 0, 1)) {
+- asm volatile ("pause");
+- }
+-}
+-
+-
+-inline void release(int* lock)
+-{
+- // Unlock via a compare-and-swap so we get a memory barrier too.
+- bool unlocked = __sync_bool_compare_and_swap(lock, 1, 0);
+- assert(unlocked);
+-}
+-
+-
+-template <typename T>
+-void select(
+- const Future<T>& future,
+- std::tr1::shared_ptr<Promise<Future<T > > > promise)
+-{
+- // We never fail the future associated with our promise.
+- assert(!promise->future().isFailed());
+-
+- if (promise->future().isPending()) { // No-op if it's discarded.
+- if (future.isReady()) { // We only set the promise if a future is ready.
+- promise->set(future);
+- }
+- }
+-}
+-
+-} // namespace internal {
+-
+-
+-// TODO(benh): Move select and discard into 'futures' namespace.
+-
+-// Returns a future that captures any ready future in a set. Note that
+-// select DOES NOT capture a future that has failed or been discarded.
+-template <typename T>
+-Future<Future<T> > select(const std::set<Future<T> >& futures)
+-{
+- std::tr1::shared_ptr<Promise<Future<T> > > promise(
+- new Promise<Future<T> >());
+-
+- Future<Future<T> > future = promise->future();
+-
+- std::tr1::function<void(const Future<T>&)> select =
+- std::tr1::bind(&internal::select<T>,
+- std::tr1::placeholders::_1,
+- promise);
+-
+- typename std::set<Future<T> >::iterator iterator;
+- for (iterator = futures.begin(); iterator != futures.end(); ++iterator) {
+- (*iterator).onAny(std::tr1::bind(select, std::tr1::placeholders::_1));
+- }
+-
+- return future;
+-}
+-
+-
+-template <typename T>
+-void discard(const std::set<Future<T> >& futures)
+-{
+- typename std::set<Future<T> >::const_iterator iterator;
+- for (iterator = futures.begin(); iterator != futures.end(); ++iterator) {
+- Future<T> future = *iterator; // Need a non-const copy to discard.
+- future.discard();
+- }
+-}
+-
+-
+-template <typename T>
+-void discard(const std::list<Future<T> >& futures)
+-{
+- typename std::list<Future<T> >::const_iterator iterator;
+- for (iterator = futures.begin(); iterator != futures.end(); ++iterator) {
+- Future<T> future = *iterator; // Need a non-const copy to discard.
+- future.discard();
+- }
+-}
+-
+-
+-template <class T>
+-void fail(const std::vector<Promise<T>*>& promises, const std::string& message)
+-{
+- typename std::vector<Promise<T>*>::const_iterator iterator;
+- for (iterator = promises.begin(); iterator != promises.end(); ++iterator) {
+- Promise<T>* promise = *iterator;
+- promise->fail(message);
+- }
+-}
+-
+-
+-template <class T>
+-void fail(const std::list<Promise<T>*>& promises, const std::string& message)
+-{
+- typename std::list<Promise<T>*>::const_iterator iterator;
+- for (iterator = promises.begin(); iterator != promises.end(); ++iterator) {
+- Promise<T>* promise = *iterator;
+- promise->fail(message);
+- }
+-}
+-
+-
+-template <typename T>
+-Future<T> Future<T>::failed(const std::string& message)
+-{
+- Future<T> future;
+- future.fail(message);
+- return future;
+-}
+-
+-
+-template <typename T>
+-Future<T>::Data::Data()
+- : lock(0),
+- latch(NULL),
+- state(PENDING),
+- t(NULL),
+- message(NULL) {}
+-
+-
+-template <typename T>
+-Future<T>::Data::~Data()
+-{
+- delete latch;
+- delete t;
+- delete message;
+-}
+-
+-
+-template <typename T>
+-Future<T>::Future()
+- : data(new Data()) {}
+-
+-
+-template <typename T>
+-Future<T>::Future(const T& _t)
+- : data(new Data())
+-{
+- set(_t);
+-}
+-
+-
+-template <typename T>
+-Future<T>::Future(const Future<T>& that)
+- : data(that.data) {}
+-
+-
+-template <typename T>
+-Future<T>::~Future()
+-{
+- if (data.unique()) {
+- discard();
+- }
+-}
+-
+-
+-template <typename T>
+-Future<T>& Future<T>::operator = (const Future<T>& that)
+-{
+- if (this != &that) {
+- if (data.unique()) {
+- discard();
+- }
+- data = that.data;
+- }
+- return *this;
+-}
+-
+-
+-template <typename T>
+-bool Future<T>::operator == (const Future<T>& that) const
+-{
+- return data == that.data;
+-}
+-
+-
+-template <typename T>
+-bool Future<T>::operator < (const Future<T>& that) const
+-{
+- return data < that.data;
+-}
+-
+-
+-template <typename T>
+-bool Future<T>::discard()
+-{
+- bool result = false;
+-
+- internal::acquire(&data->lock);
+- {
+- if (data->state == PENDING) {
+- data->state = DISCARDED;
+- if (data->latch != NULL) {
+- data->latch->trigger();
+- }
+- result = true;
+- }
+- }
+- internal::release(&data->lock);
+-
+- // Invoke all callbacks associated with this future being
+- // DISCARDED. We don't need a lock because the state is now in
+- // DISCARDED so there should not be any concurrent modifications.
+- if (result) {
+- while (!data->onDiscardedCallbacks.empty()) {
+- // TODO(*): Invoke callbacks in another execution context.
+- data->onDiscardedCallbacks.front()();
+- data->onDiscardedCallbacks.pop();
+- }
+-
+- while (!data->onAnyCallbacks.empty()) {
+- // TODO(*): Invoke callbacks in another execution context.
+- data->onAnyCallbacks.front()(*this);
+- data->onAnyCallbacks.pop();
+- }
+- }
+-
+- return result;
+-}
+-
+-
+-template <typename T>
+-bool Future<T>::isPending() const
+-{
+- return data->state == PENDING;
+-}
+-
+-
+-template <typename T>
+-bool Future<T>::isReady() const
+-{
+- return data->state == READY;
+-}
+-
+-
+-template <typename T>
+-bool Future<T>::isDiscarded() const
+-{
+- return data->state == DISCARDED;
+-}
+-
+-
+-template <typename T>
+-bool Future<T>::isFailed() const
+-{
+- return data->state == FAILED;
+-}
+-
+-
+-template <typename T>
+-bool Future<T>::await(const Duration& duration) const
+-{
+- bool await = false;
+-
+- internal::acquire(&data->lock);
+- {
+- if (data->state == PENDING) {
+- if (data->latch == NULL) {
+- data->latch = new Latch();
+- }
+- await = true;
+- }
+- }
+- internal::release(&data->lock);
+-
+- if (await) {
+- return data->latch->await(duration);
+- }
+-
+- return true;
+-}
+-
+-
+-template <typename T>
+-T Future<T>::get() const
+-{
+- if (!isReady()) {
+- await();
+- }
+-
+- CHECK(!isPending()) << "Future was in PENDING after await()";
+-
+- if (!isReady()) {
+- if (isFailed()) {
+- std::cerr << "Future::get() but state == FAILED: "
+- << failure() << std::endl;
+- } else if (isDiscarded()) {
+- std::cerr << "Future::get() but state == DISCARDED" << std::endl;
+- }
+- abort();
+- }
+-
+- assert(data->t != NULL);
+- return *data->t;
+-}
+-
+-
+-template <typename T>
+-std::string Future<T>::failure() const
+-{
+- if (data->message != NULL) {
+- return *data->message;
+- }
+- return "";
+-}
+-
+-
+-template <typename T>
+-const Future<T>& Future<T>::onReady(const ReadyCallback& callback) const
+-{
+- bool run = false;
+-
+- internal::acquire(&data->lock);
+- {
+- if (data->state == READY) {
+- run = true;
+- } else if (data->state == PENDING) {
+- data->onReadyCallbacks.push(callback);
+- }
+- }
+- internal::release(&data->lock);
+-
+- // TODO(*): Invoke callback in another execution context.
+- if (run) {
+- callback(*data->t);
+- }
+-
+- return *this;
+-}
+-
+-
+-template <typename T>
+-const Future<T>& Future<T>::onFailed(const FailedCallback& callback) const
+-{
+- bool run = false;
+-
+- internal::acquire(&data->lock);
+- {
+- if (data->state == FAILED) {
+- run = true;
+- } else if (data->state == PENDING) {
+- data->onFailedCallbacks.push(callback);
+- }
+- }
+- internal::release(&data->lock);
+-
+- // TODO(*): Invoke callback in another execution context.
+- if (run) {
+- callback(*data->message);
+- }
+-
+- return *this;
+-}
+-
+-
+-template <typename T>
+-const Future<T>& Future<T>::onDiscarded(
+- const DiscardedCallback& callback) const
+-{
+- bool run = false;
+-
+- internal::acquire(&data->lock);
+- {
+- if (data->state == DISCARDED) {
+- run = true;
+- } else if (data->state == PENDING) {
+- data->onDiscardedCallbacks.push(callback);
+- }
+- }
+- internal::release(&data->lock);
+-
+- // TODO(*): Invoke callback in another execution context.
+- if (run) {
+- callback();
+- }
+-
+- return *this;
+-}
+-
+-
+-template <typename T>
+-const Future<T>& Future<T>::onAny(const AnyCallback& callback) const
+-{
+- bool run = false;
+-
+- internal::acquire(&data->lock);
+- {
+- if (data->state != PENDING) {
+- run = true;
+- } else if (data->state == PENDING) {
+- data->onAnyCallbacks.push(callback);
+- }
+- }
+- internal::release(&data->lock);
+-
+- // TODO(*): Invoke callback in another execution context.
+- if (run) {
+- callback(*this);
+- }
+-
+- return *this;
+-}
+-
+-
+-namespace internal {
+-
+-template <typename T, typename X>
+-void thenf(const std::tr1::shared_ptr<Promise<X> >& promise,
+- const std::tr1::function<Future<X>(const T&)>& f,
+- const Future<T>& future)
+-{
+- if (future.isReady()) {
+- promise->associate(f(future.get()));
+- } else if (future.isFailed()) {
+- promise->fail(future.failure());
+- } else if (future.isDiscarded()) {
+- promise->future().discard();
+- }
+-}
+-
+-
+-template <typename T, typename X>
+-void then(const std::tr1::shared_ptr<Promise<X> >& promise,
+- const std::tr1::function<X(const T&)>& f,
+- const Future<T>& future)
+-{
+- if (future.isReady()) {
+- promise->set(f(future.get()));
+- } else if (future.isFailed()) {
+- promise->fail(future.failure());
+- } else if (future.isDiscarded()) {
+- promise->future().discard();
+- }
+-}
+-
+-} // namespace internal {
+-
+-
+-template <typename T>
+-template <typename X>
+-Future<X> Future<T>::then(const std::tr1::function<Future<X>(const T&)>& f) const
+-{
+- std::tr1::shared_ptr<Promise<X> > promise(new Promise<X>());
+-
+- std::tr1::function<void(const Future<T>&)> thenf =
+- std::tr1::bind(&internal::thenf<T, X>,
+- promise,
+- f,
+- std::tr1::placeholders::_1);
+-
+- onAny(thenf);
+-
+- // Propagate discarding up the chain (note that we bind with a copy
+- // of this future since 'this' might no longer be valid but other
+- // references might still exist.
+- // TODO(benh): Need to pass 'future' as a weak_ptr so that we can
+- // avoid reference counting cycles!
+- std::tr1::function<void(void)> discard =
+- std::tr1::bind(&Future<T>::discard, *this);
+-
+- promise->future().onDiscarded(discard);
+-
+- return promise->future();
+-}
+-
+-
+-template <typename T>
+-template <typename X>
+-Future<X> Future<T>::then(const std::tr1::function<X(const T&)>& f) const
+-{
+- std::tr1::shared_ptr<Promise<X> > promise(new Promise<X>());
+-
+- std::tr1::function<void(const Future<T>&)> then =
+- std::tr1::bind(&internal::then<T, X>,
+- promise,
+- f,
+- std::tr1::placeholders::_1);
+-
+- onAny(then);
+-
+- // Propagate discarding up the chain (note that we bind with a copy
+- // of this future since 'this' might no longer be valid but other
+- // references might still exist.
+- // TODO(benh): Need to pass 'future' as a weak_ptr so that we can
+- // avoid reference counting cycles!
+- std::tr1::function<void(void)> discard =
+- std::tr1::bind(&Future<T>::discard, *this);
+-
+- promise->future().onDiscarded(discard);
+-
+- return promise->future();
+-}
+-
+-
+-#if __cplusplus >= 201103L
+-template <typename T>
+-template <typename F>
+-auto Future<T>::then(F f) const
+- -> typename internal::wrap<decltype(f(T()))>::Type
+-{
+- typedef typename internal::unwrap<decltype(f(T()))>::Type X;
+-
+- std::tr1::shared_ptr<Promise<X>> promise(new Promise<X>());
+-
+- onAny([=] (const Future<T>& future) {
+- if (future.isReady()) {
+- promise->set(f(future.get()));
+- } else if (future.isFailed()) {
+- promise->fail(future.failure());
+- } else if (future.isDiscarded()) {
+- promise->future().discard();
+- }
+- });
+-
+- // TODO(benh): Need to use weak_ptr here so that we can avoid
+- // reference counting cycles!
+- Future<T> future(*this);
+-
+- promise->future().onDiscarded([=] () {
+- future.discard(); // Need a non-const copy to discard.
+- });
+-
+- return promise->future();
+-}
+-#endif
+-
+-
+-template <typename T>
+-bool Future<T>::set(const T& _t)
+-{
+- bool result = false;
+-
+- internal::acquire(&data->lock);
+- {
+- if (data->state == PENDING) {
+- data->t = new T(_t);
+- data->state = READY;
+- if (data->latch != NULL) {
+- data->latch->trigger();
+- }
+- result = true;
+- }
+- }
+- internal::release(&data->lock);
+-
+- // Invoke all callbacks associated with this future being READY. We
+- // don't need a lock because the state is now in READY so there
+- // should not be any concurrent modications.
+- if (result) {
+- while (!data->onReadyCallbacks.empty()) {
+- // TODO(*): Invoke callbacks in another execution context.
+- data->onReadyCallbacks.front()(*data->t);
+- data->onReadyCallbacks.pop();
+- }
+-
+- while (!data->onAnyCallbacks.empty()) {
+- // TODO(*): Invoke callbacks in another execution context.
+- data->onAnyCallbacks.front()(*this);
+- data->onAnyCallbacks.pop();
+- }
+- }
+-
+- return result;
+-}
+-
+-
+-template <typename T>
+-bool Future<T>::fail(const std::string& _message)
+-{
+- bool result = false;
+-
+- internal::acquire(&data->lock);
+- {
+- if (data->state == PENDING) {
+- data->message = new std::string(_message);
+- data->state = FAILED;
+- if (data->latch != NULL) {
+- data->latch->trigger();
+- }
+- result = true;
+- }
+- }
+- internal::release(&data->lock);
+-
+- // Invoke all callbacks associated with this future being FAILED. We
+- // don't need a lock because the state is now in FAILED so there
+- // should not be any concurrent modications.
+- if (result) {
+- while (!data->onFailedCallbacks.empty()) {
+- // TODO(*): Invoke callbacks in another execution context.
+- data->onFailedCallbacks.front()(*data->message);
+- data->onFailedCallbacks.pop();
+- }
+-
+- while (!data->onAnyCallbacks.empty()) {
+- // TODO(*): Invoke callbacks in another execution context.
+- data->onAnyCallbacks.front()(*this);
+- data->onAnyCallbacks.pop();
+- }
+- }
+-
+- return result;
+-}
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_FUTURE_HPP__
+diff --git a/3rdparty/libprocess/include/process/gc.hpp b/3rdparty/libprocess/include/process/gc.hpp
+deleted file mode 100644
+index e83c636..0000000
+--- a/3rdparty/libprocess/include/process/gc.hpp
++++ /dev/null
+@@ -1,46 +0,0 @@
+-#ifndef __PROCESS_GC_HPP__
+-#define __PROCESS_GC_HPP__
+-
+-#include <map>
+-
+-#include <process/process.hpp>
+-
+-
+-namespace process {
+-
+-class GarbageCollector : public Process<GarbageCollector>
+-{
+-public:
+- GarbageCollector() : ProcessBase("__gc__") {}
+- virtual ~GarbageCollector() {}
+-
+- template <typename T>
+- void manage(const T* t)
+- {
+- const ProcessBase* process = t;
+- if (process != NULL) {
+- processes[process->self()] = process;
+- link(process->self());
+- }
+- }
+-
+-protected:
+- virtual void exited(const UPID& pid)
+- {
+- if (processes.count(pid) > 0) {
+- const ProcessBase* process = processes[pid];
+- processes.erase(pid);
+- delete process;
+- }
+- }
+-
+-private:
+- std::map<UPID, const ProcessBase*> processes;
+-};
+-
+-
+-extern PID<GarbageCollector> gc;
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_GC_HPP__
+diff --git a/3rdparty/libprocess/include/process/gmock.hpp b/3rdparty/libprocess/include/process/gmock.hpp
+deleted file mode 100644
+index a8cab4c..0000000
+--- a/3rdparty/libprocess/include/process/gmock.hpp
++++ /dev/null
+@@ -1,327 +0,0 @@
+-#ifndef __PROCESS_GMOCK_HPP__
+-#define __PROCESS_GMOCK_HPP__
+-
+-#include <pthread.h>
+-
+-#include <gmock/gmock.h>
+-
+-#include <tr1/tuple>
+-
+-#include <process/dispatch.hpp>
+-#include <process/event.hpp>
+-#include <process/filter.hpp>
+-#include <process/pid.hpp>
+-
+-#include <stout/exit.hpp>
+-#include <stout/nothing.hpp>
+-
+-
+-// THIS IS DEPRECATED AND BROKEN! REPLACE ALL USES!
+-#define EXPECT_MESSAGE(name, from, to) \
+- EXPECT_CALL(*new process::MockFilter(), \
+- filter(testing::A<const process::MessageEvent&>())) \
+- .With(process::MessageMatcher(name, from, to))
+-
+-
+-// THIS IS DEPRECATED AND BROKEN! REPLACE ALL USES!
+-#define EXPECT_DISPATCH(pid, method) \
+- EXPECT_CALL(*new process::MockFilter(), \
+- filter(testing::A<const process::DispatchEvent&>())) \
+- .With(process::DispatchMatcher(pid, method))
+-
+-
+-#define FUTURE_MESSAGE(name, from, to) \
+- process::FutureMessage(name, from, to)
+-
+-#define DROP_MESSAGE(name, from, to) \
+- process::FutureMessage(name, from, to, true)
+-
+-#define FUTURE_DISPATCH(pid, method) \
+- process::FutureDispatch(pid, method)
+-
+-#define DROP_DISPATCH(pid, method) \
+- process::FutureDispatch(pid, method, true)
+-
+-#define DROP_MESSAGES(name, from, to) \
+- process::DropMessages(name, from, to)
+-
+-#define DROP_DISPATCHES(pid, method) \
+- process::DropDispatches(pid, method)
+-
+-
+-ACTION_TEMPLATE(PromiseArg,
+- HAS_1_TEMPLATE_PARAMS(int, k),
+- AND_1_VALUE_PARAMS(promise))
+-{
+- // TODO(benh): Use a shared_ptr for promise to defend against this
+- // action getting invoked more than once (e.g., used via
+- // WillRepeatedly). We won't be able to set it a second time but at
+- // least we won't get a segmentation fault. We could also consider
+- // warning users if they attempted to set it more than once.
+- promise->set(std::tr1::get<k>(args));
+- delete promise;
+-}
+-
+-
+-template <int index, typename T>
+-PromiseArgActionP<index, process::Promise<T>*> FutureArg(
+- process::Future<T>* future)
+-{
+- process::Promise<T>* promise = new process::Promise<T>();
+- *future = promise->future();
+- return PromiseArg<index>(promise);
+-}
+-
+-
+-ACTION_TEMPLATE(PromiseArgField,
+- HAS_1_TEMPLATE_PARAMS(int, k),
+- AND_2_VALUE_PARAMS(field, promise))
+-{
+- // TODO(benh): Use a shared_ptr for promise to defend against this
+- // action getting invoked more than once (e.g., used via
+- // WillRepeatedly). We won't be able to set it a second time but at
+- // least we won't get a segmentation fault. We could also consider
+- // warning users if they attempted to set it more than once.
+- promise->set(*(std::tr1::get<k>(args).*field));
+- delete promise;
+-}
+-
+-
+-template <int index, typename Field, typename T>
+-PromiseArgFieldActionP2<index, Field, process::Promise<T>*> FutureArgField(
+- Field field,
+- process::Future<T>* future)
+-{
+- process::Promise<T>* promise = new process::Promise<T>();
+- *future = promise->future();
+- return PromiseArgField<index>(field, promise);
+-}
+-
+-
+-ACTION_P2(PromiseSatisfy, promise, value)
+-{
+- promise->set(value);
+- delete promise;
+-}
+-
+-
+-template <typename T>
+-PromiseSatisfyActionP2<process::Promise<T>*, T> FutureSatisfy(
+- process::Future<T>* future,
+- T t)
+-{
+- process::Promise<T>* promise = new process::Promise<T>();
+- *future = promise->future();
+- return PromiseSatisfy(promise, t);
+-}
+-
+-
+-inline PromiseSatisfyActionP2<process::Promise<Nothing>*, Nothing>
+-FutureSatisfy(process::Future<Nothing>* future)
+-{
+- process::Promise<Nothing>* promise = new process::Promise<Nothing>();
+- *future = promise->future();
+- return PromiseSatisfy(promise, Nothing());
+-}
+-
+-
+-namespace process {
+-
+-class MockFilter : public Filter
+-{
+-public:
+- MockFilter()
+- {
+- EXPECT_CALL(*this, filter(testing::A<const MessageEvent&>()))
+- .WillRepeatedly(testing::Return(false));
+- EXPECT_CALL(*this, filter(testing::A<const DispatchEvent&>()))
+- .WillRepeatedly(testing::Return(false));
+- EXPECT_CALL(*this, filter(testing::A<const HttpEvent&>()))
+- .WillRepeatedly(testing::Return(false));
+- EXPECT_CALL(*this, filter(testing::A<const ExitedEvent&>()))
+- .WillRepeatedly(testing::Return(false));
+- }
+-
+- MOCK_METHOD1(filter, bool(const MessageEvent&));
+- MOCK_METHOD1(filter, bool(const DispatchEvent&));
+- MOCK_METHOD1(filter, bool(const HttpEvent&));
+- MOCK_METHOD1(filter, bool(const ExitedEvent&));
+-};
+-
+-
+-// A definition of a libprocess filter to enable waiting for events
+-// (such as messages or dispatches) via in tests. This is not meant to
+-// be used directly by tests; tests should use macros like
+-// FUTURE_MESSAGE and FUTURE_DISPATCH instead.
+-class TestsFilter : public Filter
+-{
+-public:
+- TestsFilter()
+- {
+- // We use a recursive mutex here in the event that satisfying the
+- // future created in FutureMessage or FutureDispatch via the
+- // FutureArgField or FutureSatisfy actions invokes callbacks (from
+- // Future::then or Future::onAny, etc) that themselves invoke
+- // FutureDispatch or FutureMessage.
+- pthread_mutexattr_t attr;
+- pthread_mutexattr_init(&attr);
+- pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+- pthread_mutex_init(&mutex, &attr);
+- pthread_mutexattr_destroy(&attr);
+- }
+-
+- virtual bool filter(const MessageEvent& event) { return handle(event); }
+- virtual bool filter(const DispatchEvent& event) { return handle(event); }
+- virtual bool filter(const HttpEvent& event) { return handle(event); }
+- virtual bool filter(const ExitedEvent& event) { return handle(event); }
+-
+- template <typename T>
+- bool handle(const T& t)
+- {
+- pthread_mutex_lock(&mutex);
+- bool drop = mock.filter(t);
+- pthread_mutex_unlock(&mutex);
+- return drop;
+- }
+-
+- MockFilter mock;
+- pthread_mutex_t mutex;;
+-};
+-
+-
+-class FilterTestEventListener : public ::testing::EmptyTestEventListener
+-{
+-public:
+- // Returns the singleton instance of the listener.
+- static FilterTestEventListener* instance()
+- {
+- static FilterTestEventListener* listener = new FilterTestEventListener();
+- return listener;
+- }
+-
+- // Installs and returns the filter, creating it if necessary.
+- TestsFilter* install()
+- {
+- if (!started) {
+- EXIT(1)
+- << "To use FUTURE/DROP_MESSAGE/DISPATCH, etc. you need to do the "
+- << "following before you invoke RUN_ALL_TESTS():\n\n"
+- << "\t::testing::TestEventListeners& listeners =\n"
+- << "\t ::testing::UnitTest::GetInstance()->listeners();\n"
+- << "\tlisteners.Append(process::FilterTestEventListener::instance());";
+- }
+-
+- if (filter != NULL) {
+- return filter;
+- }
+-
+- filter = new TestsFilter();
+-
+- // Set the filter in libprocess.
+- process::filter(filter);
+-
+- return filter;
+- }
+-
+- virtual void OnTestProgramStart(const ::testing::UnitTest&)
+- {
+- started = true;
+- }
+-
+- virtual void OnTestEnd(const ::testing::TestInfo&)
+- {
+- if (filter != NULL) {
+- // Remove the filter in libprocess _before_ deleting.
+- process::filter(NULL);
+- delete filter;
+- filter = NULL;
+- }
+- }
+-
+-private:
+- FilterTestEventListener() : filter(NULL), started(false) {}
+-
+- TestsFilter* filter;
+-
+- // Indicates if we got the OnTestProgramStart callback in order to
+- // detect if we have been properly added as a listener.
+- bool started;
+-};
+-
+-
+-MATCHER_P3(MessageMatcher, name, from, to, "")
+-{
+- const MessageEvent& event = ::std::tr1::get<0>(arg);
+- return (testing::Matcher<std::string>(name).Matches(event.message->name) &&
+- testing::Matcher<UPID>(from).Matches(event.message->from) &&
+- testing::Matcher<UPID>(to).Matches(event.message->to));
+-}
+-
+-
+-MATCHER_P2(DispatchMatcher, pid, method, "")
+-{
+- const DispatchEvent& event = ::std::tr1::get<0>(arg);
+- return (testing::Matcher<UPID>(pid).Matches(event.pid) &&
+- testing::Matcher<std::string>(internal::canonicalize(method))
+- .Matches(event.method));
+-}
+-
+-
+-template <typename Name, typename From, typename To>
+-Future<Message> FutureMessage(Name name, From from, To to, bool drop = false)
+-{
+- TestsFilter* filter = FilterTestEventListener::instance()->install();
+- pthread_mutex_lock(&filter->mutex);
+- Future<Message> future;
+- EXPECT_CALL(filter->mock, filter(testing::A<const MessageEvent&>()))
+- .With(MessageMatcher(name, from, to))
+- .WillOnce(testing::DoAll(FutureArgField<0>(&MessageEvent::message, &future),
+- testing::Return(drop)))
+- .RetiresOnSaturation(); // Don't impose any subsequent expectations.
+- pthread_mutex_unlock(&filter->mutex);
+- return future;
+-}
+-
+-
+-template <typename PID, typename Method>
+-Future<Nothing> FutureDispatch(PID pid, Method method, bool drop = false)
+-{
+- TestsFilter* filter = FilterTestEventListener::instance()->install();
+- pthread_mutex_lock(&filter->mutex);
+- Future<Nothing> future;
+- EXPECT_CALL(filter->mock, filter(testing::A<const DispatchEvent&>()))
+- .With(DispatchMatcher(pid, method))
+- .WillOnce(testing::DoAll(FutureSatisfy(&future),
+- testing::Return(drop)))
+- .RetiresOnSaturation(); // Don't impose any subsequent expectations.
+- pthread_mutex_unlock(&filter->mutex);
+- return future;
+-}
+-
+-
+-template <typename Name, typename From, typename To>
+-void DropMessages(Name name, From from, To to)
+-{
+- TestsFilter* filter = FilterTestEventListener::instance()->install();
+- pthread_mutex_lock(&filter->mutex);
+- EXPECT_CALL(filter->mock, filter(testing::A<const MessageEvent&>()))
+- .With(MessageMatcher(name, from, to))
+- .WillRepeatedly(testing::Return(true));
+- pthread_mutex_unlock(&filter->mutex);
+-}
+-
+-
+-template <typename PID, typename Method>
+-void DropDispatches(PID pid, Method method)
+-{
+- TestsFilter* filter = FilterTestEventListener::instance()->install();
+- pthread_mutex_lock(&filter->mutex);
+- EXPECT_CALL(filter->mock, filter(testing::A<const DispatchEvent&>()))
+- .With(DispatchMatcher(pid, method))
+- .WillRepeatedly(testing::Return(true));
+- pthread_mutex_unlock(&filter->mutex);
+-}
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_GMOCK_HPP__
+diff --git a/3rdparty/libprocess/include/process/gtest.hpp b/3rdparty/libprocess/include/process/gtest.hpp
+deleted file mode 100644
+index 753ef74..0000000
+--- a/3rdparty/libprocess/include/process/gtest.hpp
++++ /dev/null
+@@ -1,338 +0,0 @@
+-#ifndef __PROCESS_GTEST_HPP__
+-#define __PROCESS_GTEST_HPP__
+-
+-#include <gtest/gtest.h>
+-
+-#include <string>
+-
+-#include <process/clock.hpp>
+-#include <process/future.hpp>
+-#include <process/http.hpp>
+-
+-#include <stout/duration.hpp>
+-#include <stout/option.hpp>
+-
+-namespace process {
+-
+-// A simple test event listener that makes sure to resume the clock
+-// after each test even if the previous test had a partial result
+-// (i.e., an ASSERT_* failed).
+-class ClockTestEventListener : public ::testing::EmptyTestEventListener
+-{
+-public:
+- // Returns the singleton instance of the listener.
+- static ClockTestEventListener* instance()
+- {
+- static ClockTestEventListener* listener = new ClockTestEventListener();
+- return listener;
+- }
+-
+- virtual void OnTestEnd(const ::testing::TestInfo&)
+- {
+- if (process::Clock::paused()) {
+- process::Clock::resume();
+- }
+- }
+-private:
+- ClockTestEventListener() {}
+-};
+-
+-} // namespace process {
+-
+-template <typename T>
+-::testing::AssertionResult AwaitAssertReady(
+- const char* expr,
+- const char*, // Unused string representation of 'duration'.
+- const process::Future<T>& actual,
+- const Duration& duration)
+-{
+- if (!actual.await(duration)) {
+- return ::testing::AssertionFailure()
+- << "Failed to wait " << duration << " for " << expr;
+- } else if (actual.isDiscarded()) {
+- return ::testing::AssertionFailure()
+- << expr << " was discarded";
+- } else if (actual.isFailed()) {
+- return ::testing::AssertionFailure()
+- << "(" << expr << ").failure(): " << actual.failure();
+- }
+-
+- return ::testing::AssertionSuccess();
+-}
+-
+-
+-template <typename T>
+-::testing::AssertionResult AwaitAssertFailed(
+- const char* expr,
+- const char*, // Unused string representation of 'duration'.
+- const process::Future<T>& actual,
+- const Duration& duration)
+-{
+- if (!actual.await(duration)) {
+- return ::testing::AssertionFailure()
+- << "Failed to wait " << duration << " for " << expr;
+- } else if (actual.isDiscarded()) {
+- return ::testing::AssertionFailure()
+- << expr << " was discarded";
+- } else if (actual.isReady()) {
+- return ::testing::AssertionFailure()
+- << expr << " is ready (" << ::testing::PrintToString(actual.get()) << ")";
+- }
+-
+- return ::testing::AssertionSuccess();
+-}
+-
+-
+-template <typename T>
+-::testing::AssertionResult AwaitAssertDiscarded(
+- const char* expr,
+- const char*, // Unused string representation of 'duration'.
+- const process::Future<T>& actual,
+- const Duration& duration)
+-{
+- if (!actual.await(duration)) {
+- return ::testing::AssertionFailure()
+- << "Failed to wait " << duration << " for " << expr;
+- } else if (actual.isFailed()) {
+- return ::testing::AssertionFailure()
+- << "(" << expr << ").failure(): " << actual.failure();
+- } else if (actual.isReady()) {
+- return ::testing::AssertionFailure()
+- << expr << " is ready (" << ::testing::PrintToString(actual.get()) << ")";
+- }
+-
+- return ::testing::AssertionSuccess();
+-}
+-
+-
+-template <typename T1, typename T2>
+-::testing::AssertionResult AwaitAssertEq(
+- const char* expectedExpr,
+- const char* actualExpr,
+- const char* durationExpr,
+- const T1& expected,
+- const process::Future<T2>& actual,
+- const Duration& duration)
+-{
+- const ::testing::AssertionResult result =
+- AwaitAssertReady(actualExpr, durationExpr, actual, duration);
+-
+- if (result) {
+- if (expected == actual.get()) {
+- return ::testing::AssertionSuccess();
+- } else {
+- return ::testing::AssertionFailure()
+- << "Value of: (" << actualExpr << ").get()\n"
+- << " Actual: " << ::testing::PrintToString(actual.get()) << "\n"
+- << "Expected: " << expectedExpr << "\n"
+- << "Which is: " << ::testing::PrintToString(expected);
+- }
+- }
+-
+- return result;
+-}
+-
+-
+-#define AWAIT_ASSERT_READY_FOR(actual, duration) \
+- ASSERT_PRED_FORMAT2(AwaitAssertReady, actual, duration)
+-
+-
+-#define AWAIT_ASSERT_READY(actual) \
+- AWAIT_ASSERT_READY_FOR(actual, Seconds(10))
+-
+-
+-#define AWAIT_READY_FOR(actual, duration) \
+- AWAIT_ASSERT_READY_FOR(actual, duration)
+-
+-
+-#define AWAIT_READY(actual) \
+- AWAIT_ASSERT_READY(actual)
+-
+-
+-#define AWAIT_EXPECT_READY_FOR(actual, duration) \
+- EXPECT_PRED_FORMAT2(AwaitAssertReady, actual, duration)
+-
+-
+-#define AWAIT_EXPECT_READY(actual) \
+- AWAIT_EXPECT_READY_FOR(actual, Seconds(10))
+-
+-
+-#define AWAIT_ASSERT_FAILED_FOR(actual, duration) \
+- ASSERT_PRED_FORMAT2(AwaitAssertFailed, actual, duration)
+-
+-
+-#define AWAIT_ASSERT_FAILED(actual) \
+- AWAIT_ASSERT_FAILED_FOR(actual, Seconds(10))
+-
+-
+-#define AWAIT_FAILED_FOR(actual, duration) \
+- AWAIT_ASSERT_FAILED_FOR(actual, duration)
+-
+-
+-#define AWAIT_FAILED(actual) \
+- AWAIT_ASSERT_FAILED(actual)
+-
+-
+-#define AWAIT_EXPECT_FAILED_FOR(actual, duration) \
+- EXPECT_PRED_FORMAT2(AwaitAssertFailed, actual, duration)
+-
+-
+-#define AWAIT_EXPECT_FAILED(actual) \
+- AWAIT_EXPECT_FAILED_FOR(actual, Seconds(10))
+-
+-
+-#define AWAIT_ASSERT_DISCARDED_FOR(actual, duration) \
+- ASSERT_PRED_FORMAT2(AwaitAssertDiscarded, actual, duration)
+-
+-
+-#define AWAIT_ASSERT_DISCARDED(actual) \
+- AWAIT_ASSERT_DISCARDED_FOR(actual, Seconds(10))
+-
+-
+-#define AWAIT_DISCARDED_FOR(actual, duration) \
+- AWAIT_ASSERT_DISCARDED_FOR(actual, duration)
+-
+-
+-#define AWAIT_DISCARDED(actual) \
+- AWAIT_ASSERT_DISCARDED(actual)
+-
+-
+-#define AWAIT_EXPECT_DISCARDED_FOR(actual, duration) \
+- EXPECT_PRED_FORMAT2(AwaitAssertDiscarded, actual, duration)
+-
+-
+-#define AWAIT_EXPECT_DISCARDED(actual) \
+- AWAIT_EXPECT_DISCARDED_FOR(actual, Seconds(10))
+-
+-
+-#define AWAIT_ASSERT_EQ_FOR(expected, actual, duration) \
+- ASSERT_PRED_FORMAT3(AwaitAssertEq, expected, actual, duration)
+-
+-
+-#define AWAIT_ASSERT_EQ(expected, actual) \
+- AWAIT_ASSERT_EQ_FOR(expected, actual, Seconds(10))
+-
+-
+-#define AWAIT_EQ(expected, actual) \
+- AWAIT_ASSERT_EQ(expected, actual)
+-
+-
+-#define AWAIT_EXPECT_EQ_FOR(expected, actual, duration) \
+- EXPECT_PRED_FORMAT3(AwaitAssertEq, expected, actual, duration)
+-
+-
+-#define AWAIT_EXPECT_EQ(expected, actual) \
+- AWAIT_EXPECT_EQ_FOR(expected, actual, Seconds(10))
+-
+-
+-inline ::testing::AssertionResult AwaitAssertResponseStatusEq(
+- const char* expectedExpr,
+- const char* actualExpr,
+- const char* durationExpr,
+- const std::string& expected,
+- const process::Future<process::http::Response>& actual,
+- const Duration& duration)
+-{
+- const ::testing::AssertionResult result =
+- AwaitAssertReady(actualExpr, durationExpr, actual, duration);
+-
+- if (result) {
+- if (expected == actual.get().status) {
+- return ::testing::AssertionSuccess();
+- } else {
+- return ::testing::AssertionFailure()
+- << "Value of: (" << actualExpr << ").get().status\n"
+- << " Actual: " << ::testing::PrintToString(actual.get().status) << "\n"
+- << "Expected: " << expectedExpr << "\n"
+- << "Which is: " << ::testing::PrintToString(expected);
+- }
+- }
+-
+- return result;
+-}
+-
+-
+-#define AWAIT_EXPECT_RESPONSE_STATUS_EQ_FOR(expected, actual, duration) \
+- EXPECT_PRED_FORMAT3(AwaitAssertResponseStatusEq, expected, actual, duration)
+-
+-
+-#define AWAIT_EXPECT_RESPONSE_STATUS_EQ(expected, actual) \
+- AWAIT_EXPECT_RESPONSE_STATUS_EQ_FOR(expected, actual, Seconds(10))
+-
+-
+-inline ::testing::AssertionResult AwaitAssertResponseBodyEq(
+- const char* expectedExpr,
+- const char* actualExpr,
+- const char* durationExpr,
+- const std::string& expected,
+- const process::Future<process::http::Response>& actual,
+- const Duration& duration)
+-{
+- const ::testing::AssertionResult result =
+- AwaitAssertReady(actualExpr, durationExpr, actual, duration);
+-
+- if (result) {
+- if (expected == actual.get().body) {
+- return ::testing::AssertionSuccess();
+- } else {
+- return ::testing::AssertionFailure()
+- << "Value of: (" << actualExpr << ").get().body\n"
+- << " Actual: " << ::testing::PrintToString(actual.get().body) << "\n"
+- << "Expected: " << expectedExpr << "\n"
+- << "Which is: " << ::testing::PrintToString(expected);
+- }
+- }
+-
+- return result;
+-}
+-
+-
+-#define AWAIT_EXPECT_RESPONSE_BODY_EQ_FOR(expected, actual, duration) \
+- EXPECT_PRED_FORMAT3(AwaitAssertResponseBodyEq, expected, actual, duration)
+-
+-
+-#define AWAIT_EXPECT_RESPONSE_BODY_EQ(expected, actual) \
+- AWAIT_EXPECT_RESPONSE_BODY_EQ_FOR(expected, actual, Seconds(10))
+-
+-
+-inline ::testing::AssertionResult AwaitAssertResponseHeaderEq(
+- const char* expectedExpr,
+- const char* keyExpr,
+- const char* actualExpr,
+- const char* durationExpr,
+- const std::string& expected,
+- const std::string& key,
+- const process::Future<process::http::Response>& actual,
+- const Duration& duration)
+-{
+- const ::testing::AssertionResult result =
+- AwaitAssertReady(actualExpr, durationExpr, actual, duration);
+-
+- if (result) {
+- const Option<std::string> value = actual.get().headers.get(key);
+- if (value.isNone()) {
+- return ::testing::AssertionFailure()
+- << "Response does not contain header '" << key << "'";
+- } else if (expected == value.get()) {
+- return ::testing::AssertionSuccess();
+- } else {
+- return ::testing::AssertionFailure()
+- << "Value of: (" << actualExpr << ").get().headers[" << keyExpr << "]\n"
+- << " Actual: " << ::testing::PrintToString(value.get()) << "\n"
+- << "Expected: " << expectedExpr << "\n"
+- << "Which is: " << ::testing::PrintToString(expected);
+- }
+- }
+-
+- return result;
+-}
+-
+-
+-#define AWAIT_EXPECT_RESPONSE_HEADER_EQ_FOR(expected, key, actual, duration) \
+- EXPECT_PRED_FORMAT4(AwaitAssertResponseHeaderEq, expected, key, actual, duration)
+-
+-
+-#define AWAIT_EXPECT_RESPONSE_HEADER_EQ(expected, key, actual) \
+- AWAIT_EXPECT_RESPONSE_HEADER_EQ_FOR(expected, key, actual, Seconds(10))
+-
+-#endif // __PROCESS_GTEST_HPP__
+diff --git a/3rdparty/libprocess/include/process/help.hpp b/3rdparty/libprocess/include/process/help.hpp
+deleted file mode 100644
+index 7876a34..0000000
+--- a/3rdparty/libprocess/include/process/help.hpp
++++ /dev/null
+@@ -1,278 +0,0 @@
+-#ifndef __PROCESS_HELP_HPP__
+-#define __PROCESS_HELP_HPP__
+-
+-#include <map>
+-#include <string>
+-#include <vector>
+-
+-#include <process/future.hpp>
+-#include <process/http.hpp>
+-#include <process/process.hpp>
+-
+-#include <stout/foreach.hpp>
+-#include <stout/json.hpp>
+-#include <stout/option.hpp>
+-#include <stout/preprocessor.hpp>
+-#include <stout/stringify.hpp>
+-#include <stout/strings.hpp>
+-
+-namespace process {
+-
+-// Constructs a Markdown based help "page" for a route with the
+-// following template:
+-//
+-// ### TL;DR; ###
+-// tldr
+-//
+-// ### USAGE ###
+-// usage
+-//
+-// ### DESCRIPTION ###
+-// description
+-//
+-// references
+-//
+-// See the 'TLDR', 'USAGE', 'DESCRIPTION', and 'REFERENCES' helpers
+-// below to more easily construct your help pages.
+-inline std::string HELP(
+- std::string tldr,
+- std::string usage,
+- std::string description,
+- const Option<std::string>& references = None())
+-{
+- // Make sure 'tldr', 'usage', and 'description' end with a newline.
+- if (!strings::endsWith(tldr, "\n")) {
+- tldr += "\n";
+- }
+-
+- if (!strings::endsWith(usage, "\n")) {
+- usage += "\n";
+- }
+-
+- if (!strings::endsWith(description, "\n")) {
+- description += "\n";
+- }
+-
+- // Construct the help string.
+- std::string help =
+- "### TL;DR; ###\n" +
+- tldr +
+- "\n" +
+- "### USAGE ###\n" +
+- usage +
+- "\n" +
+- "### DESCRIPTION ###\n" +
+- description;
+-
+- if (references.isSome()) {
+- help += "\n";
+- help += references.get();
+- }
+-
+- return help;
+-}
+-
+-
+-// Helper for single-line TL;DR; that adds a newline.
+-inline std::string TLDR(const std::string& tldr)
+-{
+- return tldr + "\n";
+-}
+-
+-
+-// Helper for single-line usage that puts it in a blockquote as code
+-// and adds a newline.
+-inline std::string USAGE(const std::string& usage)
+-{
+- return "> " + usage + "\n";
+-}
+-
+-
+-// Helpers for adding newlines to each line of a multi-line
+-// description or references.
+-#define LINE_TEMPLATE(Z, N, DATA) + CAT(line, N) + "\n"
+-#define TEMPLATE(Z, N, DATA) \
+- inline std::string DESCRIPTION( \
+- ENUM_PARAMS(N, const std::string& line)) \
+- { \
+- return \
+- "" \
+- REPEAT_FROM_TO(0, N, LINE_TEMPLATE, _); \
+- } \
+- \
+- \
+- inline std::string REFERENCES( \
+- ENUM_PARAMS(N, const std::string& line)) \
+- { \
+- return \
+- "" \
+- REPEAT_FROM_TO(0, N, LINE_TEMPLATE, _); \
+- }
+-
+- REPEAT_FROM_TO(1, 201, TEMPLATE, _) // Lines 1 -> 200.
+-#undef TEMPLATE
+-#undef LINE_TEMPLATE
+-
+-
+-// Help process for serving /help, /help/id, and /help/id/name (see
+-// Help::help below for more information).
+-class Help : public Process<Help>
+-{
+-public:
+- Help() : ProcessBase("help") {}
+-
+- // Adds 'help' for the route 'name' of the process with the
+- // specified 'id' (i.e., 'http://ip:port/id/name'). It's expected
+- // that 'help' is written using Markdown. When serving help to a
+- // browser the Markdown will be rendered into HTML while a tool like
+- // 'curl' or 'http' will just be given the Markdown directly (thus
+- // making it easy to get help without opening a browser).
+- // NOTE: There is no need to dispatch this directly; this gets
+- // automagically dispatched by 'ProcessBase::route'.
+- void add(const std::string& id,
+- const std::string& name,
+- const Option<std::string>& help)
+- {
+- if (id != "help") { // TODO(benh): Enable help for help.
+- if (help.isSome()) {
+- helps[id][name] = help.get();
+- } else {
+- helps[id][name] = "## No help page for `/" + id + name + "`\n";
+- }
+- route("/" + id, "Help for " + id, &Help::help);
+- }
+- }
+-
+-protected:
+- virtual void initialize()
+- {
+- route("/", None(), &Help::help);
+- }
+-
+-private:
+- // Handles the following:
+- //
+- // (1) http://ip:port/help
+- // (2) http://ip:port/help/id
+- // (3) http://ip:port/help/id/name
+- //
+- // Where 'id' and 'name' are replaced with a process ID and route
+- // name respectively. (1) provides a "table of contents" for all
+- // available processes while (2) provides a "table of contents" for
+- // all endpoints associated with a particular process and (3)
+- // provides the help associated with a particular endpoint of a
+- // process.
+- Future<http::Response> help(const http::Request& request)
+- {
+- // Split the path by '/'.
+- std::vector<std::string> tokens = strings::tokenize(request.path, "/");
+-
+- Option<std::string> id = None();
+- Option<std::string> name = None();
+-
+- if (tokens.size() > 3) {
+- return http::BadRequest("Malformed URL, expecting '/help/id/name/'\n");
+- } else if (tokens.size() == 3) {
+- id = tokens[1];
+- name = tokens[2];
+- } else if (tokens.size() > 1) {
+- id = tokens[1];
+- }
+-
+- std::string document;
+- std::string references;
+-
+- if (id.isNone()) { // http://ip:port/help
+- document += "## HELP\n";
+- foreachkey (const std::string& id, helps) {
+- document += "> [/" + id + "][" + id + "]\n";
+- references += "[" + id + "]: /help/" + id + "\n";
+- }
+- } else if (name.isNone()) { // http://ip:port/help/id
+- if (helps.count(id.get()) == 0) {
+- return http::BadRequest(
+- "No help available for '/" + id.get() + "'.\n");
+- }
+-
+- document += "## `/" + id.get() + "` ##\n";
+- foreachkey (const std::string& name, helps[id.get()]) {
+- const std::string& path = id.get() + name;
+- document += "> [/" + path + "][" + path + "]\n";
+- references += "[" + path + "]: /help/" + path + "\n";
+- }
+- } else { // http://ip:port/help/id/name
+- if (helps.count(id.get()) == 0) {
+- return http::BadRequest(
+- "No help available for '/" + id.get() + "'.\n");
+- } else if (helps[id.get()].count("/" + name.get()) == 0) {
+- return http::BadRequest(
+- "No help available for '/" + id.get() + "/" + name.get() + "'.\n");
+- }
+-
+- document += helps[id.get()]["/" + name.get()];
+- }
+-
+- // Final Markdown is 'document' followed by the 'references'.
+- std::string markdown = document + "\n" + references;
+-
+- // Just send the Markdown if we aren't speaking to a browser. For
+- // now we only check for the 'curl' or 'http' utilities.
+- Option<std::string> agent = request.headers.get("User-Agent");
+-
+- if (agent.isSome() &&
+- (strings::startsWith(agent.get(), "curl") ||
+- strings::startsWith(agent.get(), "HTTPie"))) {
+- http::Response response = http::OK(markdown);
+- response.headers["Content-Type"] = "text/x-markdown";
+- return response;
+- }
+-
+- // Need to JSONify the markdown for embedding into JavaScript.
+- markdown = stringify(JSON::String(markdown));
+-
+- // Provide some JavaScript to render the Markdown into some aesthetically
+- // pleasing HTML. ;)
+- return http::OK(
+- "<html>"
+- "<head>"
+- "<title>Help</title>"
+- "<script src=\"/static/js/marked.min.js\"></script>"
+- "<script>"
+- " function loaded() {"
+- " marked.setOptions({ breaks: true });"
+- " document.body.innerHTML = marked(" + markdown + ");"
+- " }"
+- "</script>"
+- "<style>"
+- "body {"
+- " font-family: Helvetica, arial, sans-serif;"
+- " font-size: 14px;"
+- " line-height: 1.6;"
+- " padding-top: 10px;"
+- " padding-bottom: 10px;"
+- " background-color: white;"
+- " padding: 30px;"
+- "}"
+- "blockquote {"
+- " border-left: 5px solid #dddddd;"
+- " padding: 0 10px;"
+- " color: #777777;"
+- " margin: 0 0 20px;"
+- "}"
+- "a {"
+- " color: #0088cc;"
+- " text-decoration: none;"
+- "}"
+- "</style>"
+- "</head>"
+- "<body onload=\"loaded()\">"
+- "</body>"
+- "</html>");
+- }
+-
+- std::map<std::string, std::map<std::string, std::string> > helps;
+-};
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_HELP_HPP__
+diff --git a/3rdparty/libprocess/include/process/http.hpp b/3rdparty/libprocess/include/process/http.hpp
+deleted file mode 100644
+index 5bdd520..0000000
+--- a/3rdparty/libprocess/include/process/http.hpp
++++ /dev/null
+@@ -1,541 +0,0 @@
+-#ifndef __PROCESS_HTTP_HPP__
+-#define __PROCESS_HTTP_HPP__
+-
+-#include <cctype>
+-#include <cstdlib>
+-#include <iomanip>
+-#include <sstream>
+-#include <string>
+-
+-#include <limits.h>
+-
+-#include <process/future.hpp>
+-#include <process/pid.hpp>
+-
+-#include <stout/error.hpp>
+-#include <stout/hashmap.hpp>
+-#include <stout/json.hpp>
+-#include <stout/none.hpp>
+-#include <stout/option.hpp>
+-#include <stout/stringify.hpp>
+-#include <stout/strings.hpp>
+-#include <stout/try.hpp>
+-
+-namespace process {
+-namespace http {
+-
+-struct Request
+-{
+- // TODO(benh): Add major/minor version.
+- // TODO(bmahler): Header names are not case sensitive! Either make these
+- // case-insensitive, or add a variable for each header in HTTP 1.0/1.1 (like
+- // we've done here with keepAlive).
+- // Tracked by: https://issues.apache.org/jira/browse/MESOS-328.
+- hashmap<std::string, std::string> headers;
+- std::string method;
+- std::string url; // path?query#fragment
+- std::string path;
+- std::string fragment;
+- hashmap<std::string, std::string> query;
+- std::string body;
+- bool keepAlive;
+-
+- // Returns whether the encoding is considered acceptable in the request.
+- // TODO(bmahler): Consider this logic being in decoder.hpp, and having the
+- // Request contain a member variable for each popular HTTP 1.0/1.1 header.
+- bool accepts(const std::string& encoding) const
+- {
+- // See RFC 2616, section 14.3 for the details.
+- Option<std::string> accepted = headers.get("Accept-Encoding");
+-
+- if (accepted.isNone()) {
+- return false;
+- }
+-
+- // Remove spaces and tabs for easier parsing.
+- accepted = strings::remove(accepted.get(), " ");
+- accepted = strings::remove(accepted.get(), "\t");
+- accepted = strings::remove(accepted.get(), "\n");
+-
+- // From RFC 2616:
+- // 1. If the content-coding is one of the content-codings listed in
+- // the Accept-Encoding field, then it is acceptable, unless it is
+- // accompanied by a qvalue of 0. (As defined in section 3.9, a
+- // qvalue of 0 means "not acceptable.")
+- // 2. The special "*" symbol in an Accept-Encoding field matches any
+- // available content-coding not explicitly listed in the header
+- // field.
+-
+- // First we'll look for the encoding specified explicitly, then '*'.
+- std::vector<std::string> candidates;
+- candidates.push_back(encoding); // Rule 1.
+- candidates.push_back("*"); // Rule 2.
+-
+- foreach (std::string& candidate, candidates) {
+- // Is the candidate one of the accepted encodings?
+- foreach (const std::string& _encoding,
+- strings::tokenize(accepted.get(), ",")) {
+- if (strings::startsWith(_encoding, candidate)) {
+- // Is there a 0 q value? Ex: 'gzip;q=0.0'.
+- const std::map<std::string, std::vector<std::string> >& values =
+- strings::pairs(_encoding, ";", "=");
+-
+- // Look for { "q": ["0"] }.
+- if (values.count("q") == 0 || values.find("q")->second.size() != 1) {
+- // No q value, or malformed q value.
+- return true;
+- }
+-
+- // Is the q value > 0?
+- Try<double> value = numify<double>(values.find("q")->second[0]);
+- return value.isSome() && value.get() > 0;
+- }
+- }
+- }
+-
+- // NOTE: 3 and 4 are partially ignored since we can only provide gzip.
+- // 3. If multiple content-codings are acceptable, then the acceptable
+- // content-coding with the highest non-zero qvalue is preferred.
+- // 4. The "identity" content-coding is always acceptable, unless
+- // specifically refused because the Accept-Encoding field includes
+- // "identity;q=0", or because the field includes "*;q=0" and does
+- // not explicitly include the "identity" content-coding. If the
+- // Accept-Encoding field-value is empty, then only the "identity"
+- // encoding is acceptable.
+- return false;
+- }
+-};
+-
+-
+-struct Response
+-{
+- Response()
+- : type(NONE)
+- {}
+-
+- Response(const std::string& _body)
+- : type(BODY),
+- body(_body)
+- {
+- headers["Content-Length"] = stringify(body.size());
+- }
+-
+- // TODO(benh): Add major/minor version.
+- std::string status;
+- hashmap<std::string, std::string> headers;
+-
+- // Either provide a "body", an absolute "path" to a file, or a
+- // "pipe" for streaming a response. Distinguish between the cases
+- // using 'type' below.
+- //
+- // BODY: Uses 'body' as the body of the response. These may be
+- // encoded using gzip for efficiency, if 'Content-Encoding' is not
+- // already specified.
+- //
+- // PATH: Attempts to perform a 'sendfile' operation on the file
+- // found at 'path'.
+- //
+- // PIPE: Splices data from 'pipe' using 'Transfer-Encoding=chunked'.
+- // Note that the read end of the pipe will be closed by libprocess
+- // either after the write end has been closed or if the socket the
+- // data is being spliced to has been closed (i.e., nobody is
+- // listening any longer). This can cause writes to the pipe to
+- // generate a SIGPIPE (which will terminate your program unless you
+- // explicitly ignore them or handle them).
+- //
+- // In all cases (BODY, PATH, PIPE), you are expected to properly
+- // specify the 'Content-Type' header, but the 'Content-Length' and
+- // or 'Transfer-Encoding' headers will be filled in for you.
+- enum {
+- NONE,
+- BODY,
+- PATH,
+- PIPE
+- } type;
+-
+- std::string body;
+- std::string path;
+- int pipe; // See comment above regarding the semantics for closing.
+-};
+-
+-
+-struct OK : Response
+-{
+- OK()
+- {
+- status = "200 OK";
+- }
+-
+- OK(const char* body) : Response(std::string(body))
+- {
+- status = "200 OK";
+- }
+-
+- OK(const std::string& body) : Response(body)
+- {
+- status = "200 OK";
+- }
+-
+- OK(const JSON::Value& value, const Option<std::string>& jsonp = None())
+- {
+- type = BODY;
+-
+- status = "200 OK";
+-
+- std::ostringstream out;
+-
+- if (jsonp.isSome()) {
+- out << jsonp.get() << "(";
+- }
+-
+- JSON::render(out, value);
+-
+- if (jsonp.isSome()) {
+- out << ");";
+- headers["Content-Type"] = "text/javascript";
+- } else {
+- headers["Content-Type"] = "application/json";
+- }
+-
+- headers["Content-Length"] = stringify(out.str().size());
+- body = out.str().data();
+- }
+-};
+-
+-
+-struct TemporaryRedirect : Response
+-{
+- TemporaryRedirect(const std::string& url)
+- {
+- status = "307 Temporary Redirect";
+- headers["Location"] = url;
+- }
+-};
+-
+-
+-struct BadRequest : Response
+-{
+- BadRequest()
+- {
+- status = "400 Bad Request";
+- }
+-
+- BadRequest(const std::string& body)
+- : Response(body)
+- {
+- status = "400 Bad Request";
+- }
+-};
+-
+-
+-struct NotFound : Response
+-{
+- NotFound()
+- {
+- status = "404 Not Found";
+- }
+-
+- NotFound(const std::string& body) : Response(body)
+- {
+- status = "404 Not Found";
+- }
+-};
+-
+-
+-struct InternalServerError : Response
+-{
+- InternalServerError()
+- {
+- status = "500 Internal Server Error";
+- }
+-
+- InternalServerError(const std::string& body) : Response(body)
+- {
+- status = "500 Internal Server Error";
+- }
+-};
+-
+-
+-struct ServiceUnavailable : Response
+-{
+- ServiceUnavailable()
+- {
+- status = "503 Service Unavailable";
+- }
+-
+- ServiceUnavailable(const std::string& body) : Response(body)
+- {
+- status = "503 Service Unavailable";
+- }
+-};
+-
+-
+-namespace path {
+-
+-// Parses an HTTP path into a map given a pattern (TODO(benh): Make
+-// the patterns be regular expressions). This returns an error if
+-// 'pattern' doesn't match 'path'. For example:
+-//
+-// parse("/books/{isbn}/chapters/{chapter}",
+-// "/books/0304827484/chapters/3")
+-//
+-// Would return a map with the following:
+-// books: "books"
+-// isbn: "0304827484"
+-// chapters: "chapters"
+-// chapter: "3"
+-//
+-// Another example:
+-//
+-// parse("/books/{isbn}/chapters/{chapter}",
+-// "/books/0304827484")
+-//
+-// Would return a map with the following:
+-// books: "books"
+-// isbn: "0304827484"
+-//
+-// And another:
+-//
+-// parse("/books/{isbn}/chapters/{chapter}",
+-// "/books/0304827484/chapters")
+-//
+-// Would return a map with the following:
+-// books: "books"
+-// isbn: "0304827484"
+-// chapters: "chapters"
+-inline Try<hashmap<std::string, std::string> > parse(
+- const std::string& pattern,
+- const std::string& path)
+-{
+- // Split the pattern by '/' into keys.
+- std::vector<std::string> keys = strings::tokenize(pattern, "/");
+-
+- // Split the path by '/' into segments.
+- std::vector<std::string> segments = strings::tokenize(path, "/");
+-
+- hashmap<std::string, std::string> result;
+-
+- while (!segments.empty()) {
+- if (keys.empty()) {
+- return Error(
+- "Not expecting suffix '" + strings::join("/", segments) + "'");
+- }
+-
+- std::string key = keys.front();
+-
+- if (strings::startsWith(key, "{") &&
+- strings::endsWith(key, "}")) {
+- key = strings::remove(key, "{", strings::PREFIX);
+- key = strings::remove(key, "}", strings::SUFFIX);
+- } else if (key != segments.front()) {
+- return Error("Expecting '" + key + "' not '" + segments.front() + "'");
+- }
+-
+- result[key] = segments.front();
+-
+- keys.erase(keys.begin());
+- segments.erase(segments.begin());
+- }
+-
+- return result;
+-}
+-
+-} // namespace path {
+-
+-
+-namespace query {
+-
+-// Parses an HTTP query string into a map. For example:
+-//
+-// parse("foo=1;bar=2;baz;foo=3")
+-//
+-// Would return a map with the following:
+-// bar: "2"
+-// baz: ""
+-// foo: "3"
+-//
+-// We use the last value for a key for simplicity, since the RFC does not
+-// specify how to handle duplicate keys:
+-// http://en.wikipedia.org/wiki/Query_string
+-// TODO(bmahler): If needed, investigate populating the query map inline
+-// for better performance.
+-inline hashmap<std::string, std::string> parse(const std::string& query)
+-{
+- hashmap<std::string, std::string> result;
+-
+- const std::vector<std::string>& tokens = strings::tokenize(query, ";&");
+- foreach (const std::string& token, tokens) {
+- const std::vector<std::string>& pairs = strings::split(token, "=");
+- if (pairs.size() == 2) {
+- result[pairs[0]] = pairs[1];
+- } else if (pairs.size() == 1) {
+- result[pairs[0]] = "";
+- }
+- }
+-
+- return result;
+-}
+-
+-} // namespace query {
+-
+-
+-// Returns a percent-encoded string according to RFC 3986.
+-// The input string must not already be percent encoded.
+-inline std::string encode(const std::string& s)
+-{
+- std::ostringstream out;
+-
+- foreach (unsigned char c, s) {
+- switch (c) {
+- // Reserved characters.
+- case '$':
+- case '&':
+- case '+':
+- case ',':
+- case '/':
+- case ':':
+- case ';':
+- case '=':
+- case '?':
+- case '@':
+- // Unsafe characters.
+- case ' ':
+- case '"':
+- case '<':
+- case '>':
+- case '#':
+- case '%':
+- case '{':
+- case '}':
+- case '|':
+- case '\\':
+- case '^':
+- case '~':
+- case '[':
+- case ']':
+- case '`':
+- // NOTE: The cast to unsigned int is needed.
+- out << '%' << std::setfill('0') << std::setw(2) << std::hex
+- << std::uppercase << (unsigned int) c;
+- break;
+- default:
+- // ASCII control characters and non-ASCII characters.
+- // NOTE: The cast to unsigned int is needed.
+- if (c < 0x20 || c > 0x7F) {
+- out << '%' << std::setfill('0') << std::setw(2) << std::hex
+- << std::uppercase << (unsigned int) c;
+- } else {
+- out << c;
+- }
+- break;
+- }
+- }
+-
+- return out.str();
+-}
+-
+-
+-// Decodes a percent-encoded string according to RFC 3986.
+-// The input string must not already be decoded.
+-// Returns error on the occurrence of a malformed % escape in s.
+-inline Try<std::string> decode(const std::string& s)
+-{
+- std::ostringstream out;
+-
+- for (size_t i = 0; i < s.length(); ++i) {
+- if (s[i] != '%') {
+- out << s[i];
+- continue;
+- }
+-
+- // We now expect two more characters: % HEXDIG HEXDIG
+- if (i + 2 >= s.length() || !isxdigit(s[i+1]) || !isxdigit(s[i+2])) {
+- return Error(
+- "Malformed % escape in '" + s + "': '" + s.substr(i, 3) + "'");
+- }
+-
+- // Convert from HEXDIG HEXDIG to char value.
+- std::istringstream in(s.substr(i + 1, 2));
+- unsigned long l;
+- in >> std::hex >> l;
+- if (l > UCHAR_MAX) {
+- std::cerr << "Unexpected conversion from hex string: "
+- << s.substr(i + 1, 2) << " to unsigned long: "
+- << l << std::endl;
+- abort();
+- }
+- out << static_cast<unsigned char>(l);
+-
+- i += 2;
+- }
+-
+- return out.str();
+-}
+-
+-
+-// Sends a blocking HTTP GET request to the process with the given upid.
+-// Returns the HTTP response from the process, read asynchronously.
+-//
+-// TODO(bmahler): Have the request sent asynchronously as well.
+-// TODO(bmahler): For efficiency, this should properly use the ResponseDecoder
+-// on the read stream, rather than parsing the full string response at the end.
+-Future<Response> get(
+- const UPID& upid,
+- const std::string& path = "",
+- const std::string& query = "");
+-
+-
+-// Status code reason strings, from the HTTP1.1 RFC:
+-// http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html
+-extern hashmap<uint16_t, std::string> statuses;
+-
+-
+-inline void initialize()
+-{
+- statuses[100] = "100 Continue";
+- statuses[101] = "101 Switching Protocols";
+- statuses[200] = "200 OK";
+- statuses[201] = "201 Created";
+- statuses[202] = "202 Accepted";
+- statuses[203] = "203 Non-Authoritative Information";
+- statuses[204] = "204 No Content";
+- statuses[205] = "205 Reset Content";
+- statuses[206] = "206 Partial Content";
+- statuses[300] = "300 Multiple Choices";
+- statuses[301] = "301 Moved Permanently";
+- statuses[302] = "302 Found";
+- statuses[303] = "303 See Other";
+- statuses[304] = "304 Not Modified";
+- statuses[305] = "305 Use Proxy";
+- statuses[307] = "307 Temporary Redirect";
+- statuses[400] = "400 Bad Request";
+- statuses[401] = "401 Unauthorized";
+- statuses[402] = "402 Payment Required";
+- statuses[403] = "403 Forbidden";
+- statuses[404] = "404 Not Found";
+- statuses[405] = "405 Method Not Allowed";
+- statuses[406] = "406 Not Acceptable";
+- statuses[407] = "407 Proxy Authentication Required";
+- statuses[408] = "408 Request Time-out";
+- statuses[409] = "409 Conflict";
+- statuses[410] = "410 Gone";
+- statuses[411] = "411 Length Required";
+- statuses[412] = "412 Precondition Failed";
+- statuses[413] = "413 Request Entity Too Large";
+- statuses[414] = "414 Request-URI Too Large";
+- statuses[415] = "415 Unsupported Media Type";
+- statuses[416] = "416 Requested range not satisfiable";
+- statuses[417] = "417 Expectation Failed";
+- statuses[500] = "500 Internal Server Error";
+- statuses[501] = "501 Not Implemented";
+- statuses[502] = "502 Bad Gateway";
+- statuses[503] = "503 Service Unavailable";
+- statuses[504] = "504 Gateway Time-out";
+- statuses[505] = "505 HTTP Version not supported";
+-}
+-
+-
+-} // namespace http {
+-} // namespace process {
+-
+-#endif // __PROCESS_HTTP_HPP__
+diff --git a/3rdparty/libprocess/include/process/id.hpp b/3rdparty/libprocess/include/process/id.hpp
+deleted file mode 100644
+index 8c256b9..0000000
+--- a/3rdparty/libprocess/include/process/id.hpp
++++ /dev/null
+@@ -1,16 +0,0 @@
+-#ifndef __PROCESS_ID_HPP__
+-#define __PROCESS_ID_HPP__
+-
+-#include <string>
+-
+-namespace process {
+-namespace ID {
+-
+-// Returns 'prefix(N)' where N represents the number of instances
+-// where this prefix has been used to generate an ID.
+-std::string generate(const std::string& prefix = "");
+-
+-} // namespace ID {
+-} // namespace process {
+-
+-#endif // __PROCESS_ID_HPP__
+diff --git a/3rdparty/libprocess/include/process/io.hpp b/3rdparty/libprocess/include/process/io.hpp
+deleted file mode 100644
+index 8cf3244..0000000
+--- a/3rdparty/libprocess/include/process/io.hpp
++++ /dev/null
+@@ -1,44 +0,0 @@
+-#ifndef __PROCESS_IO_HPP__
+-#define __PROCESS_IO_HPP__
+-
+-#include <cstring> // For size_t.
+-#include <string>
+-
+-#include <process/future.hpp>
+-
+-namespace process {
+-namespace io {
+-
+-// Possible events for polling.
+-const short READ = 0x01;
+-const short WRITE = 0x02;
+-
+-// Buffered read chunk size. Roughly 16 pages.
+-const size_t BUFFERED_READ_SIZE = 16*4096;
+-
+-// TODO(benh): Add a version which takes multiple file descriptors.
+-// Returns the events (a subset of the events specified) that can be
+-// performed on the specified file descriptor without blocking.
+-Future<short> poll(int fd, short events);
+-
+-
+-// Performs a single non-blocking read by polling on the specified file
+-// descriptor until any data can be be read. The future will become ready when
+-// some data is read (may be less than that specified by size). A future failure
+-// will be returned if an error is detected. If end-of-file is reached, value
+-// zero will be returned. Note that the return type of this function differs
+-// from the standard 'read'. In particular, this function returns the number of
+-// bytes read or zero on end-of-file (an error is indicated by failing the
+-// future, thus only a 'size_t' is necessary rather than a 'ssize_t').
+-Future<size_t> read(int fd, void* data, size_t size);
+-
+-
+-// Performs a series of asynchronous reads, until EOF is reached.
+-// NOTE: When using this, ensure the sender will close the connection
+-// so that EOF can be reached.
+-Future<std::string> read(int fd);
+-
+-} // namespace io {
+-} // namespace process {
+-
+-#endif // __PROCESS_IO_HPP__
+diff --git a/3rdparty/libprocess/include/process/latch.hpp b/3rdparty/libprocess/include/process/latch.hpp
+deleted file mode 100644
+index 5170aa8..0000000
+--- a/3rdparty/libprocess/include/process/latch.hpp
++++ /dev/null
+@@ -1,33 +0,0 @@
+-#ifndef __PROCESS_LATCH_HPP__
+-#define __PROCESS_LATCH_HPP__
+-
+-#include <process/pid.hpp>
+-
+-#include <stout/duration.hpp>
+-
+-namespace process {
+-
+-class Latch
+-{
+-public:
+- Latch();
+- virtual ~Latch();
+-
+- bool operator == (const Latch& that) const { return pid == that.pid; }
+- bool operator < (const Latch& that) const { return pid < that.pid; }
+-
+- void trigger();
+- bool await(const Duration& duration = Seconds(-1));
+-
+-private:
+- // Not copyable, not assignable.
+- Latch(const Latch& that);
+- Latch& operator = (const Latch& that);
+-
+- bool triggered;
+- UPID pid;
+-};
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_LATCH_HPP__
+diff --git a/3rdparty/libprocess/include/process/limiter.hpp b/3rdparty/libprocess/include/process/limiter.hpp
+deleted file mode 100644
+index bbe8226..0000000
+--- a/3rdparty/libprocess/include/process/limiter.hpp
++++ /dev/null
+@@ -1,140 +0,0 @@
+-#ifndef __PROCESS_LIMITER_HPP__
+-#define __PROCESS_LIMITER_HPP__
+-
+-#include <deque>
+-
+-#include <process/delay.hpp>
+-#include <process/dispatch.hpp>
+-#include <process/future.hpp>
+-#include <process/process.hpp>
+-#include <process/timeout.hpp>
+-
+-#include <stout/duration.hpp>
+-#include <stout/foreach.hpp>
+-#include <stout/nothing.hpp>
+-
+-namespace process {
+-
+-// Forward declaration.
+-class RateLimiterProcess;
+-
+-// Provides an abstraction that rate limits the number of "permits"
+-// that can be acquired over some duration.
+-// NOTE: Currently, each libprocess Process should use a separate
+-// RateLimiter instance. This is because if multiple processes share
+-// a RateLimiter instance, by the time a process acts on the Future
+-// returned by 'acquire()' another process might have acquired the
+-// next permit and do its rate limited operation.
+-class RateLimiter
+-{
+-public:
+- RateLimiter(int permits, const Duration& duration);
+- ~RateLimiter();
+-
+- // Returns a future that becomes ready when the permit is acquired.
+- Future<Nothing> acquire();
+-
+-private:
+- // Not copyable, not assignable.
+- RateLimiter(const RateLimiter&);
+- RateLimiter& operator = (const RateLimiter&);
+-
+- RateLimiterProcess* process;
+-};
+-
+-
+-class RateLimiterProcess : public Process<RateLimiterProcess>
+-{
+-public:
+- RateLimiterProcess(int _permits, const Duration& _duration)
+- : permits(_permits), duration(_duration)
+- {
+- CHECK_GT(permits, 0);
+- CHECK_GT(duration.secs(), 0);
+- }
+-
+- virtual void finalize()
+- {
+- foreach (Promise<Nothing>* promise, promises) {
+- promise->future().discard();
+- delete promise;
+- }
+- promises.clear();
+- }
+-
+- Future<Nothing> acquire()
+- {
+- if (!promises.empty()) {
+- // Need to wait for others to get permits first.
+- Promise<Nothing>* promise = new Promise<Nothing>();
+- promises.push_back(promise);
+- return promise->future();
+- } if (timeout.remaining() > Seconds(0)) {
+- // Need to wait a bit longer, but first one in the queue.
+- Promise<Nothing>* promise = new Promise<Nothing>();
+- promises.push_back(promise);
+- delay(timeout.remaining(), self(), &Self::_acquire);
+- return promise->future();
+- }
+-
+- // No need to wait!
+- double rate = permits / duration.secs();
+- timeout = Seconds(1) / rate;
+- return Nothing();
+- }
+-
+-private:
+- // Not copyable, not assignable.
+- RateLimiterProcess(const RateLimiterProcess&);
+- RateLimiterProcess& operator = (const RateLimiterProcess&);
+-
+- void _acquire()
+- {
+- CHECK(!promises.empty());
+-
+- Promise<Nothing>* promise = promises.front();
+- promises.pop_front();
+-
+- promise->set(Nothing());
+-
+- double rate = permits / duration.secs();
+- timeout = Seconds(1) / rate;
+-
+- // Repeat if necessary.
+- if (!promises.empty()) {
+- delay(timeout.remaining(), self(), &Self::_acquire);
+- }
+- }
+-
+- const int permits;
+- const Duration duration;
+-
+- Timeout timeout;
+-
+- std::deque<Promise<Nothing>*> promises;
+-};
+-
+-
+-inline RateLimiter::RateLimiter(int permits, const Duration& duration)
+-{
+- process = new RateLimiterProcess(permits, duration);
+- spawn(process);
+-}
+-
+-
+-inline RateLimiter::~RateLimiter()
+-{
+- terminate(process);
+- wait(process);
+- delete process;
+-}
+-
+-
+-inline Future<Nothing> RateLimiter::acquire()
+-{
+- return dispatch(process, &RateLimiterProcess::acquire);
+-}
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_LIMITER_HPP__
+diff --git a/3rdparty/libprocess/include/process/logging.hpp b/3rdparty/libprocess/include/process/logging.hpp
+deleted file mode 100644
+index f4fb619..0000000
+--- a/3rdparty/libprocess/include/process/logging.hpp
++++ /dev/null
+@@ -1,113 +0,0 @@
+-#ifndef __PROCESS_LOGGING_HPP__
+-#define __PROCESS_LOGGING_HPP__
+-
+-#include <glog/logging.h>
+-
+-#include <process/delay.hpp>
+-#include <process/future.hpp>
+-#include <process/http.hpp>
+-#include <process/process.hpp>
+-#include <process/timeout.hpp>
+-
+-#include <stout/duration.hpp>
+-#include <stout/numify.hpp>
+-#include <stout/option.hpp>
+-#include <stout/stringify.hpp>
+-#include <stout/try.hpp>
+-
+-namespace process {
+-
+-class Logging : public Process<Logging>
+-{
+-public:
+- Logging()
+- : ProcessBase("logging"),
+- original(FLAGS_v)
+- {
+- // Make sure all reads/writes can be done atomically (i.e., to
+- // make sure VLOG(*) statements don't read partial writes).
+- // TODO(benh): Use "atomics" primitives for doing reads/writes of
+- // FLAGS_v anyway to account for proper memory barriers.
+- CHECK(sizeof(FLAGS_v) == sizeof(int32_t));
+- }
+-
+- virtual ~Logging() {}
+-
+-protected:
+- virtual void initialize()
+- {
+- route("/toggle", TOGGLE_HELP, &This::toggle);
+- }
+-
+-private:
+- Future<http::Response> toggle(const http::Request& request)
+- {
+- Option<std::string> level = request.query.get("level");
+- Option<std::string> duration = request.query.get("duration");
+-
+- if (level.isNone() && duration.isNone()) {
+- return http::OK(stringify(FLAGS_v) + "\n");
+- }
+-
+- if (level.isSome() && duration.isNone()) {
+- return http::BadRequest("Expecting 'duration=value' in query.\n");
+- } else if (level.isNone() && duration.isSome()) {
+- return http::BadRequest("Expecting 'level=value' in query.\n");
+- }
+-
+- Try<int> v = numify<int>(level.get());
+-
+- if (v.isError()) {
+- return http::BadRequest(v.error() + ".\n");
+- }
+-
+- if (v.get() < 0) {
+- return http::BadRequest("Invalid level '" + stringify(v.get()) + "'.\n");
+- } else if (v.get() < original) {
+- return http::BadRequest("'" + stringify(v.get()) + "' < original level.\n");
+- }
+-
+- Try<Duration> d = Duration::parse(duration.get());
+-
+- if (d.isError()) {
+- return http::BadRequest(d.error() + ".\n");
+- }
+-
+- // Set the logging level.
+- set(v.get());
+-
+- // Start a revert timer (if necessary).
+- if (v.get() != original) {
+- timeout = d.get();
+- delay(timeout.remaining(), this, &This::revert);
+- }
+-
+- return http::OK();
+- }
+-
+- void set(int v)
+- {
+- if (FLAGS_v != v) {
+- VLOG(FLAGS_v) << "Setting verbose logging level to " << v;
+- FLAGS_v = v;
+- __sync_synchronize(); // Ensure 'FLAGS_v' visible in other threads.
+- }
+- }
+-
+- void revert()
+- {
+- if (timeout.remaining() == Seconds(0)) {
+- set(original);
+- }
+- }
+-
+- static const std::string TOGGLE_HELP;
+-
+- Timeout timeout;
+-
+- const int32_t original; // Original value of FLAGS_v.
+-};
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_LOGGING_HPP__
+diff --git a/3rdparty/libprocess/include/process/message.hpp b/3rdparty/libprocess/include/process/message.hpp
+deleted file mode 100644
+index c67c5e1..0000000
+--- a/3rdparty/libprocess/include/process/message.hpp
++++ /dev/null
+@@ -1,20 +0,0 @@
+-#ifndef __PROCESS_MESSAGE_HPP__
+-#define __PROCESS_MESSAGE_HPP__
+-
+-#include <string>
+-
+-#include <process/pid.hpp>
+-
+-namespace process {
+-
+-struct Message
+-{
+- std::string name;
+- UPID from;
+- UPID to;
+- std::string body;
+-};
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_MESSAGE_HPP__
+diff --git a/3rdparty/libprocess/include/process/mime.hpp b/3rdparty/libprocess/include/process/mime.hpp
+deleted file mode 100644
+index 0abeac1..0000000
+--- a/3rdparty/libprocess/include/process/mime.hpp
++++ /dev/null
+@@ -1,145 +0,0 @@
+-#ifndef __PROCESS_MIME_HPP__
+-#define __PROCESS_MIME_HPP__
+-
+-namespace process {
+-namespace mime {
+-
+-extern std::map<std::string, std::string> types;
+-
+-inline void initialize()
+-{
+- // These MIME types were collected via:
+- /*
+- python -c '
+- import mimetypes
+- for extension, type in mimetypes.types_map.iteritems():
+- print "types[\"%s\"] = \"%s\";" % (extension, type)
+- '
+- */
+-
+- types[".obj"] = "application/octet-stream";
+- types[".ra"] = "audio/x-pn-realaudio";
+- types[".wsdl"] = "application/xml";
+- types[".dll"] = "application/octet-stream";
+- types[".ras"] = "image/x-cmu-raster";
+- types[".ram"] = "application/x-pn-realaudio";
+- types[".bcpio"] = "application/x-bcpio";
+- types[".sh"] = "application/x-sh";
+- types[".m1v"] = "video/mpeg";
+- types[".xwd"] = "image/x-xwindowdump";
+- types[".doc"] = "application/msword";
+- types[".bmp"] = "image/x-ms-bmp";
+- types[".shar"] = "application/x-shar";
+- types[".js"] = "application/x-javascript";
+- types[".src"] = "application/x-wais-source";
+- types[".dvi"] = "application/x-dvi";
+- types[".aif"] = "audio/x-aiff";
+- types[".ksh"] = "text/plain";
+- types[".dot"] = "application/msword";
+- types[".mht"] = "message/rfc822";
+- types[".p12"] = "application/x-pkcs12";
+- types[".css"] = "text/css";
+- types[".csh"] = "application/x-csh";
+- types[".pwz"] = "application/vnd.ms-powerpoint";
+- types[".pdf"] = "application/pdf";
+- types[".cdf"] = "application/x-netcdf";
+- types[".pl"] = "text/plain";
+- types[".ai"] = "application/postscript";
+- types[".jpe"] = "image/jpeg";
+- types[".jpg"] = "image/jpeg";
+- types[".py"] = "text/x-python";
+- types[".xml"] = "text/xml";
+- types[".jpeg"] = "image/jpeg";
+- types[".ps"] = "application/postscript";
+- types[".gtar"] = "application/x-gtar";
+- types[".xpm"] = "image/x-xpixmap";
+- types[".hdf"] = "application/x-hdf";
+- types[".nws"] = "message/rfc822";
+- types[".tsv"] = "text/tab-separated-values";
+- types[".xpdl"] = "application/xml";
+- types[".p7c"] = "application/pkcs7-mime";
+- types[".eps"] = "application/postscript";
+- types[".ief"] = "image/ief";
+- types[".so"] = "application/octet-stream";
+- types[".xlb"] = "application/vnd.ms-excel";
+- types[".pbm"] = "image/x-portable-bitmap";
+- types[".texinfo"] = "application/x-texinfo";
+- types[".xls"] = "application/vnd.ms-excel";
+- types[".tex"] = "application/x-tex";
+- types[".rtx"] = "text/richtext";
+- types[".html"] = "text/html";
+- types[".aiff"] = "audio/x-aiff";
+- types[".aifc"] = "audio/x-aiff";
+- types[".exe"] = "application/octet-stream";
+- types[".sgm"] = "text/x-sgml";
+- types[".tif"] = "image/tiff";
+- types[".mpeg"] = "video/mpeg";
+- types[".ustar"] = "application/x-ustar";
+- types[".gif"] = "image/gif";
+- types[".ppt"] = "application/vnd.ms-powerpoint";
+- types[".pps"] = "application/vnd.ms-powerpoint";
+- types[".sgml"] = "text/x-sgml";
+- types[".ppm"] = "image/x-portable-pixmap";
+- types[".latex"] = "application/x-latex";
+- types[".bat"] = "text/plain";
+- types[".mov"] = "video/quicktime";
+- types[".ppa"] = "application/vnd.ms-powerpoint";
+- types[".tr"] = "application/x-troff";
+- types[".rdf"] = "application/xml";
+- types[".xsl"] = "application/xml";
+- types[".eml"] = "message/rfc822";
+- types[".nc"] = "application/x-netcdf";
+- types[".sv4cpio"] = "application/x-sv4cpio";
+- types[".bin"] = "application/octet-stream";
+- types[".h"] = "text/plain";
+- types[".tcl"] = "application/x-tcl";
+- types[".wiz"] = "application/msword";
+- types[".o"] = "application/octet-stream";
+- types[".a"] = "application/octet-stream";
+- types[".c"] = "text/plain";
+- types[".wav"] = "audio/x-wav";
+- types[".vcf"] = "text/x-vcard";
+- types[".xbm"] = "image/x-xbitmap";
+- types[".txt"] = "text/plain";
+- types[".au"] = "audio/basic";
+- types[".t"] = "application/x-troff";
+- types[".tiff"] = "image/tiff";
+- types[".texi"] = "application/x-texinfo";
+- types[".oda"] = "application/oda";
+- types[".ms"] = "application/x-troff-ms";
+- types[".rgb"] = "image/x-rgb";
+- types[".me"] = "application/x-troff-me";
+- types[".sv4crc"] = "application/x-sv4crc";
+- types[".qt"] = "video/quicktime";
+- types[".mpa"] = "video/mpeg";
+- types[".mpg"] = "video/mpeg";
+- types[".mpe"] = "video/mpeg";
+- types[".avi"] = "video/x-msvideo";
+- types[".pgm"] = "image/x-portable-graymap";
+- types[".pot"] = "application/vnd.ms-powerpoint";
+- types[".mif"] = "application/x-mif";
+- types[".roff"] = "application/x-troff";
+- types[".htm"] = "text/html";
+- types[".man"] = "application/x-troff-man";
+- types[".etx"] = "text/x-setext";
+- types[".zip"] = "application/zip";
+- types[".movie"] = "video/x-sgi-movie";
+- types[".pyc"] = "application/x-python-code";
+- types[".png"] = "image/png";
+- types[".pfx"] = "application/x-pkcs12";
+- types[".mhtml"] = "message/rfc822";
+- types[".tar"] = "application/x-tar";
+- types[".pnm"] = "image/x-portable-anymap";
+- types[".pyo"] = "application/x-python-code";
+- types[".snd"] = "audio/basic";
+- types[".cpio"] = "application/x-cpio";
+- types[".swf"] = "application/x-shockwave-flash";
+- types[".mp3"] = "audio/mpeg";
+- types[".mp2"] = "audio/mpeg";
+- types[".mp4"] = "video/mp4";
+-}
+-
+-} // } namespace mime {
+-} // } namespace process {
+-
+-#endif // __PROCESS_MIME_HPP__
+diff --git a/3rdparty/libprocess/include/process/once.hpp b/3rdparty/libprocess/include/process/once.hpp
+deleted file mode 100644
+index e85b382..0000000
+--- a/3rdparty/libprocess/include/process/once.hpp
++++ /dev/null
+@@ -1,48 +0,0 @@
+-#ifndef __PROCESS_ONCE_HPP__
+-#define __PROCESS_ONCE_HPP__
+-
+-#include <process/future.hpp>
+-
+-#include <stout/nothing.hpp>
+-
+-namespace process {
+-
+-// Provides a _blocking_ abstraction that's useful for performing a
+-// task exactly once.
+-class Once
+-{
+-public:
+- Once() {}
+-
+- // Returns true if this Once instance has already transitioned to a
+- // 'done' state (i.e., the action you wanted to perform "once" has
+- // been completed). Note that this BLOCKS until Once::done has been
+- // called.
+- bool once()
+- {
+- if (!outer.set(&inner)) {
+- inner.future().await();
+- return true;
+- }
+-
+- return false;
+- }
+-
+- // Transitions this Once instance to a 'done' state.
+- void done()
+- {
+- inner.set(Nothing());
+- }
+-
+-private:
+- // Not copyable, not assignable.
+- Once(const Once& that);
+- Once& operator = (const Once& that);
+-
+- Promise<Nothing> inner;
+- Promise<Promise<Nothing>*> outer;
+-};
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_ONCE_HPP__
+diff --git a/3rdparty/libprocess/include/process/owned.hpp b/3rdparty/libprocess/include/process/owned.hpp
+deleted file mode 100644
+index 4a03ea4..0000000
+--- a/3rdparty/libprocess/include/process/owned.hpp
++++ /dev/null
+@@ -1,166 +0,0 @@
+-#ifndef __PROCESS_OWNED_HPP__
+-#define __PROCESS_OWNED_HPP__
+-
+-#include <glog/logging.h>
+-
+-#include <boost/shared_ptr.hpp>
+-
+-namespace process {
+-
+-// Forward declaration.
+-template <typename T>
+-class Shared;
+-
+-
+-// Represents a uniquely owned pointer.
+-//
+-// TODO(bmahler): For now, Owned only provides shared_ptr semantics.
+-// When we make the switch to C++11, we will change to provide
+-// unique_ptr semantics. Consequently, each usage of Owned that
+-// invoked a copy will have to be adjusted to use move semantics.
+-template <typename T>
+-class Owned
+-{
+-public:
+- Owned();
+- explicit Owned(T* t);
+-
+- bool operator == (const Owned<T>& that) const;
+- bool operator < (const Owned<T>& that) const;
+-
+- T& operator * () const;
+- T* operator -> () const;
+- T* get() const;
+-
+- void reset();
+- void reset(T* t);
+- void swap(Owned<T>& that);
+-
+- // Converts from an owned pointer to a shared pointer. This owned
+- // pointer will be reset after this function is invoked.
+- Shared<T> share();
+-
+-private:
+- struct Data
+- {
+- Data(T* t);
+- ~Data();
+-
+- T* volatile t; // The pointer 't' is volatile.
+- };
+-
+- boost::shared_ptr<Data> data;
+-};
+-
+-
+-template <typename T>
+-Owned<T>::Owned() {}
+-
+-
+-template <typename T>
+-Owned<T>::Owned(T* t)
+-{
+- if (t != NULL) {
+- data.reset(new Data(t));
+- }
+-}
+-
+-
+-template <typename T>
+-bool Owned<T>::operator == (const Owned<T>& that) const
+-{
+- return data == that.data;
+-}
+-
+-
+-template <typename T>
+-bool Owned<T>::operator < (const Owned<T>& that) const
+-{
+- return data < that.data;
+-}
+-
+-
+-template <typename T>
+-T& Owned<T>::operator * () const
+-{
+- return *CHECK_NOTNULL(get());
+-}
+-
+-
+-template <typename T>
+-T* Owned<T>::operator -> () const
+-{
+- return CHECK_NOTNULL(get());
+-}
+-
+-
+-template <typename T>
+-T* Owned<T>::get() const
+-{
+- if (data.get() == NULL) {
+- return NULL;
+- } else {
+- CHECK(data->t != NULL) << "This owned pointer has already been shared";
+-
+- return data->t;
+- }
+-}
+-
+-
+-template <typename T>
+-void Owned<T>::reset()
+-{
+- data.reset();
+-}
+-
+-
+-template <typename T>
+-void Owned<T>::reset(T* t)
+-{
+- if (t == NULL) {
+- data.reset();
+- } else {
+- data.reset(new Data(t));
+- }
+-}
+-
+-
+-template <typename T>
+-void Owned<T>::swap(Owned<T>& that)
+-{
+- data.swap(that.data);
+-}
+-
+-
+-template <typename T>
+-Shared<T> Owned<T>::share()
+-{
+- if (data.get() == NULL) {
+- return Shared<T>(NULL);
+- }
+-
+- // Atomically set the pointer 'data->t' to NULL.
+- T* t = __sync_fetch_and_and(&data->t, NULL);
+- CHECK(t != NULL) << "The ownership of this pointer has already been shared";
+-
+- data.reset();
+- return Shared<T>(t);
+-}
+-
+-
+-template <typename T>
+-Owned<T>::Data::Data(T* _t)
+- : t(CHECK_NOTNULL(_t)) {}
+-
+-
+-template <typename T>
+-Owned<T>::Data::~Data()
+-{
+- if (t != NULL) {
+- delete t;
+- }
+-}
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_OWNED_HPP__
+diff --git a/3rdparty/libprocess/include/process/pid.hpp b/3rdparty/libprocess/include/process/pid.hpp
+deleted file mode 100644
+index 5a77dbc..0000000
+--- a/3rdparty/libprocess/include/process/pid.hpp
++++ /dev/null
+@@ -1,121 +0,0 @@
+-#ifndef __PROCESS_PID_HPP__
+-#define __PROCESS_PID_HPP__
+-
+-#include <stdint.h>
+-
+-#include <iostream>
+-#include <sstream>
+-#include <string>
+-
+-
+-namespace process {
+-
+-// Forward declaration to break cyclic dependencies.
+-class ProcessBase;
+-
+-
+-struct UPID
+-{
+- UPID()
+- : ip(0), port(0) {}
+-
+- UPID(const UPID& that)
+- : id(that.id), ip(that.ip), port(that.port) {}
+-
+- UPID(const char* id_, uint32_t ip_, uint16_t port_)
+- : id(id_), ip(ip_), port(port_) {}
+-
+- UPID(const std::string& id_, uint32_t ip_, uint16_t port_)
+- : id(id_), ip(ip_), port(port_) {}
+-
+- UPID(const char* s);
+-
+- UPID(const std::string& s);
+-
+- UPID(const ProcessBase& process);
+-
+- operator std::string () const;
+-
+- operator bool () const
+- {
+- return id != "" && ip != 0 && port != 0;
+- }
+-
+- bool operator ! () const
+- {
+- return id == "" && ip == 0 && port == 0;
+- }
+-
+- bool operator < (const UPID& that) const
+- {
+- if (this != &that) {
+- if (ip == that.ip && port == that.port)
+- return id < that.id;
+- else if (ip == that.ip && port != that.port)
+- return port < that.port;
+- else
+- return ip < that.ip;
+- }
+-
+- return false;
+- }
+-
+- bool operator == (const UPID& that) const
+- {
+- if (this != &that) {
+- return (id == that.id &&
+- ip == that.ip &&
+- port == that.port);
+- }
+-
+- return true;
+- }
+-
+- bool operator != (const UPID& that) const
+- {
+- return !(this->operator == (that));
+- }
+-
+- std::string id;
+- uint32_t ip;
+- uint16_t port;
+-};
+-
+-
+-template <typename T = ProcessBase>
+-struct PID : UPID
+-{
+- PID() : UPID() {}
+-
+- PID(const T* t) : UPID(static_cast<const ProcessBase&>(*t)) {}
+- PID(const T& t) : UPID(static_cast<const ProcessBase&>(t)) {}
+-
+- template <typename Base>
+- operator PID<Base> () const
+- {
+- // Only allow upcasts!
+- T* t = NULL;
+- Base* base = t;
+- (void)base; // Eliminate unused base warning.
+- PID<Base> pid;
+- pid.id = id;
+- pid.ip = ip;
+- pid.port = port;
+- return pid;
+- }
+-};
+-
+-
+-// Outputing UPIDs and generating UPIDs using streams.
+-std::ostream& operator << (std::ostream&, const UPID&);
+-std::istream& operator >> (std::istream&, UPID&);
+-
+-
+-// UPID hash value (for example, to use in Boost's unordered maps).
+-std::size_t hash_value(const UPID&);
+-
+-} // namespace process {
+-
+-
+-
+-#endif // __PROCESS_PID_HPP__
+diff --git a/3rdparty/libprocess/include/process/process.hpp b/3rdparty/libprocess/include/process/process.hpp
+deleted file mode 100644
+index d9dc571..0000000
+--- a/3rdparty/libprocess/include/process/process.hpp
++++ /dev/null
+@@ -1,375 +0,0 @@
+-#ifndef __PROCESS_PROCESS_HPP__
+-#define __PROCESS_PROCESS_HPP__
+-
+-#include <stdint.h>
+-#include <pthread.h>
+-
+-#include <map>
+-#include <queue>
+-
+-#include <tr1/functional>
+-
+-#include <process/clock.hpp>
+-#include <process/event.hpp>
+-#include <process/filter.hpp>
+-#include <process/http.hpp>
+-#include <process/message.hpp>
+-#include <process/mime.hpp>
+-#include <process/pid.hpp>
+-
+-#include <stout/duration.hpp>
+-#include <stout/option.hpp>
+-#include <stout/thread.hpp>
+-
+-namespace process {
+-
+-class ProcessBase : public EventVisitor
+-{
+-public:
+- ProcessBase(const std::string& id = "");
+-
+- virtual ~ProcessBase();
+-
+- UPID self() const { return pid; }
+-
+-protected:
+- // Invoked when an event is serviced.
+- virtual void serve(const Event& event)
+- {
+- event.visit(this);
+- }
+-
+- // Callbacks used to visit (i.e., handle) a specific event.
+- virtual void visit(const MessageEvent& event);
+- virtual void visit(const DispatchEvent& event);
+- virtual void visit(const HttpEvent& event);
+- virtual void visit(const ExitedEvent& event);
+- virtual void visit(const TerminateEvent& event);
+-
+- // Invoked when a process gets spawned.
+- virtual void initialize() {}
+-
+- // Invoked when a process is terminated (unless visit is overriden).
+- virtual void finalize() {}
+-
+- // Invoked when a linked process has exited (see link).
+- virtual void exited(const UPID& pid) {}
+-
+- // Invoked when a linked process can no longer be monitored (see link).
+- virtual void lost(const UPID& pid) {}
+-
+- // Puts a message at front of queue.
+- void inject(
+- const UPID& from,
+- const std::string& name,
+- const char* data = NULL,
+- size_t length = 0);
+-
+- // Sends a message with data to PID.
+- void send(
+- const UPID& to,
+- const std::string& name,
+- const char* data = NULL,
+- size_t length = 0);
+-
+- // Links with the specified PID. Linking with a process from within
+- // the same "operating system process" is gauranteed to give you
+- // perfect monitoring of that process. However, linking with a
+- // process on another machine might result in receiving lost
+- // callbacks due to the nature of a distributed environment.
+- UPID link(const UPID& pid);
+-
+- // The default visit implementation for message events invokes
+- // installed message handlers, or delegates the message to another
+- // process (a delegate can be installed below but a message handler
+- // always takes precedence over delegating). A message handler is
+- // any function which takes two arguments, the "from" pid and the
+- // message body.
+- typedef std::tr1::function<void(const UPID&, const std::string&)>
+- MessageHandler;
+-
+- // Setup a handler for a message.
+- void install(
+- const std::string& name,
+- const MessageHandler& handler)
+- {
+- handlers.message[name] = handler;
+- }
+-
+- template <typename T>
+- void install(
+- const std::string& name,
+- void (T::*method)(const UPID&, const std::string&))
+- {
+- // Note that we use dynamic_cast here so a process can use
+- // multiple inheritance if it sees so fit (e.g., to implement
+- // multiple callback interfaces).
+- MessageHandler handler =
+- std::tr1::bind(method,
+- dynamic_cast<T*>(this),
+- std::tr1::placeholders::_1,
+- std::tr1::placeholders::_2);
+- install(name, handler);
+- }
+-
+- // Delegate incoming message's with the specified name to pid.
+- void delegate(const std::string& name, const UPID& pid)
+- {
+- delegates[name] = pid;
+- }
+-
+- // The default visit implementation for HTTP events invokes
+- // installed HTTP handlers. A HTTP handler is any function which
+- // takes an http::Request object and returns an http::Response.
+- typedef std::tr1::function<Future<http::Response>(const http::Request&)>
+- HttpRequestHandler;
+-
+- // Setup a handler for an HTTP request.
+- bool route(
+- const std::string& name,
+- const Option<std::string>& help,
+- const HttpRequestHandler& handler);
+-
+- template <typename T>
+- bool route(
+- const std::string& name,
+- const Option<std::string>& help,
+- Future<http::Response> (T::*method)(const http::Request&))
+- {
+- // Note that we use dynamic_cast here so a process can use
+- // multiple inheritance if it sees so fit (e.g., to implement
+- // multiple callback interfaces).
+- HttpRequestHandler handler =
+- std::tr1::bind(method, dynamic_cast<T*>(this),
+- std::tr1::placeholders::_1);
+- return route(name, help, handler);
+- }
+-
+- // Provide the static asset(s) at the specified _absolute_ path for
+- // the specified name. For example, assuming the process named
+- // "server" invoked 'provide("name", "path")' then an HTTP request
+- // for '/server/name' would return the asset found at 'path'. If the
+- // specified path is a directory then an HTTP request for
+- // '/server/name/file' would return the asset found at
+- // '/path/file'. The 'Content-Type' header of the HTTP response will
+- // be set to the specified type given the file extension (you can
+- // manipulate this via the optional 'types' parameter).
+- void provide(
+- const std::string& name,
+- const std::string& path,
+- const std::map<std::string, std::string>& types = mime::types)
+- {
+- // TODO(benh): Check that name is only alphanumeric (i.e., has no
+- // '/') and that path is absolute.
+- Asset asset;
+- asset.path = path;
+- asset.types = types;
+- assets[name] = asset;
+- }
+-
+-private:
+- friend class SocketManager;
+- friend class ProcessManager;
+- friend class ProcessReference;
+- friend void* schedule(void*);
+-
+- // Process states.
+- enum {
+- BOTTOM,
+- READY,
+- RUNNING,
+- BLOCKED,
+- TERMINATING,
+- TERMINATED
+- } state;
+-
+- // Mutex protecting internals.
+- // TODO(benh): Consider replacing with a spinlock, on multi-core systems.
+- pthread_mutex_t m;
+- void lock() { pthread_mutex_lock(&m); }
+- void unlock() { pthread_mutex_unlock(&m); }
+-
+- // Enqueue the specified message, request, or function call.
+- void enqueue(Event* event, bool inject = false);
+-
+- // Queue of received events.
+- std::deque<Event*> events;
+-
+- // Delegates for messages.
+- std::map<std::string, UPID> delegates;
+-
+- // Handlers for messages and HTTP requests.
+- struct {
+- std::map<std::string, MessageHandler> message;
+- std::map<std::string, HttpRequestHandler> http;
+- } handlers;
+-
+- // Definition of a static asset.
+- struct Asset
+- {
+- std::string path;
+- std::map<std::string, std::string> types;
+- };
+-
+- // Static assets(s) to provide.
+- std::map<std::string, Asset> assets;
+-
+- // Active references.
+- int refs;
+-
+- // Process PID.
+- UPID pid;
+-};
+-
+-
+-template <typename T>
+-class Process : public virtual ProcessBase {
+-public:
+- virtual ~Process() {}
+-
+- // Returns pid of process; valid even before calling spawn.
+- PID<T> self() const { return PID<T>(dynamic_cast<const T*>(this)); }
+-
+-protected:
+- // Useful typedefs for dispatch/delay/defer to self()/this.
+- typedef T Self;
+- typedef T This;
+-};
+-
+-
+-/**
+- * Initialize the library. Note that libprocess uses Google's glog and
+- * you can specify options for it (e.g., a logging directory) via
+- * environment variables (see the glog documentation for more
+- * information).
+- *
+- * @param delegate process to receive root HTTP requests
+- */
+-void initialize(const std::string& delegate = "");
+-
+-
+-/**
+- * Returns the IP address associated with this instance of the
+- * library.
+- */
+-uint32_t ip();
+-
+-
+-/**
+- * Returns the port associated with this instance of the library.
+- */
+-uint16_t port();
+-
+-
+-/**
+- * Spawn a new process.
+- *
+- * @param process process to be spawned
+- * @param manage boolean whether process should get garbage collected
+- */
+-UPID spawn(ProcessBase* process, bool manage = false);
+-
+-template <typename T>
+-PID<T> spawn(T* t, bool manage = false)
+-{
+- // We save the pid before spawn is called because it's possible that
+- // the process has already been deleted after spawn returns (e.g.,
+- // if 'manage' is true).
+- PID<T> pid(t);
+-
+- if (!spawn(static_cast<ProcessBase*>(t), manage)) {
+- return PID<T>();
+- }
+-
+- return pid;
+-}
+-
+-template <typename T>
+-PID<T> spawn(T& t, bool manage = false)
+-{
+- return spawn(&t, manage);
+-}
+-
+-
+-/**
+- * Send a TERMINATE message to a process, injecting the message ahead
+- * of all other messages queued up for that process if requested. Note
+- * that currently terminate only works for local processes (in the
+- * future we plan to make this more explicit via the use of a PID
+- * instead of a UPID).
+- *
+- * @param inject if true message will be put on front of message queue
+- */
+-void terminate(const UPID& pid, bool inject = true);
+-void terminate(const ProcessBase& process, bool inject = true);
+-void terminate(const ProcessBase* process, bool inject = true);
+-
+-
+-/**
+- * Wait for process to exit no more than specified seconds (returns
+- * true if actually waited on a process).
+- *
+- * @param PID id of the process
+- * @param secs max time to wait, 0 implies wait for ever
+- */
+-bool wait(const UPID& pid, const Duration& duration = Seconds(-1));
+-bool wait(const ProcessBase& process, const Duration& duration = Seconds(-1));
+-bool wait(const ProcessBase* process, const Duration& duration = Seconds(-1));
+-
+-
+-/**
+- * Sends a message with data without a return address.
+- *
+- * @param to receiver
+- * @param name message name
+- * @param data data to send (gets copied)
+- * @param length length of data
+- */
+-void post(const UPID& to,
+- const std::string& name,
+- const char* data = NULL,
+- size_t length = 0);
+-
+-
+-void post(const UPID& from,
+- const UPID& to,
+- const std::string& name,
+- const char* data = NULL,
+- size_t length = 0);
+-
+-
+-// Inline implementations of above.
+-inline void terminate(const ProcessBase& process, bool inject)
+-{
+- terminate(process.self(), inject);
+-}
+-
+-
+-inline void terminate(const ProcessBase* process, bool inject)
+-{
+- terminate(process->self(), inject);
+-}
+-
+-
+-inline bool wait(const ProcessBase& process, const Duration& duration)
+-{
+- return process::wait(process.self(), duration); // Explicit to disambiguate.
+-}
+-
+-
+-inline bool wait(const ProcessBase* process, const Duration& duration)
+-{
+- return process::wait(process->self(), duration); // Explicit to disambiguate.
+-}
+-
+-
+-// Per thread process pointer. The extra level of indirection from
+-// _process_ to __process__ is used in order to take advantage of the
+-// ThreadLocal operators without needing the extra dereference.
+-extern ThreadLocal<ProcessBase>* _process_;
+-
+-#define __process__ (*_process_)
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_PROCESS_HPP__
+diff --git a/3rdparty/libprocess/include/process/profiler.hpp b/3rdparty/libprocess/include/process/profiler.hpp
+deleted file mode 100644
+index c886d7e..0000000
+--- a/3rdparty/libprocess/include/process/profiler.hpp
++++ /dev/null
+@@ -1,119 +0,0 @@
+-#ifndef __PROCESS_PROFILER_HPP__
+-#define __PROCESS_PROFILER_HPP__
+-
+-#include <glog/logging.h>
+-
+-#ifdef HAS_GPERFTOOLS
+-#include <gperftools/profiler.h>
+-#endif
+-
+-#include <string>
+-
+-#include <process/future.hpp>
+-#include <process/http.hpp>
+-#include <process/process.hpp>
+-
+-#include <stout/format.hpp>
+-#include <stout/os.hpp>
+-
+-namespace process {
+-
+-const std::string PROFILE_FILE = "perftools.out";
+-
+-class Profiler : public Process<Profiler>
+-{
+-public:
+- Profiler() : ProcessBase("profiler"), started(false) {}
+-
+- virtual ~Profiler() {}
+-
+-protected:
+- virtual void initialize()
+- {
+- route("/start", START_HELP, &Profiler::start);
+- route("/stop", STOP_HELP, &Profiler::stop);
+- }
+-
+-private:
+- static const std::string START_HELP;
+- static const std::string STOP_HELP;
+-
+- // HTTP endpoints.
+-
+- // Starts the profiler. There are no request parameters.
+- Future<http::Response> start(const http::Request& request)
+- {
+-#ifdef HAS_GPERFTOOLS
+- if (os::getenv("LIBPROCESS_ENABLE_PROFILER", false) != "1") {
+- return http::BadRequest(
+- "The profiler is not enabled. To enable the profiler, libprocess "
+- "must be started with LIBPROCESS_ENABLE_PROFILER=1 in the "
+- "environment.\n");
+- }
+-
+- if (started) {
+- return http::BadRequest("Profiler already started.\n");
+- }
+-
+- LOG(INFO) << "Starting Profiler";
+-
+- // WARNING: If using libunwind < 1.0.1, profiling should not be used, as
+- // there are reports of crashes.
+- // WARNING: If using libunwind 1.0.1, profiling should not be turned on
+- // when it's possible for new threads to be created.
+- // This may cause a deadlock. The workaround used in libprocess is described
+- // here:
+- // https://groups.google.com/d/topic/google-perftools/Df10Uy4Djrg/discussion
+- // NOTE: We have not tested this with libunwind > 1.0.1.
+- if (!ProfilerStart(PROFILE_FILE.c_str())) {
+- std::string error =
+- strings::format("Failed to start profiler: %s", strerror(errno)).get();
+- LOG(ERROR) << error;
+- return http::InternalServerError(error);
+- }
+-
+- started = true;
+- return http::OK("Profiler started.\n");
+-#else
+- return http::BadRequest(
+- "Perftools is disabled. To enable perftools, "
+- "configure libprocess with --enable-perftools.\n");
+-#endif
+- }
+-
+- // Stops the profiler. There are no request parameters.
+- // This returns the profile output, it will also remain present
+- // in the working directory.
+- Future<http::Response> stop(const http::Request& request)
+- {
+-#ifdef HAS_GPERFTOOLS
+- if (!started) {
+- return http::BadRequest("Profiler not running.\n");
+- }
+-
+- LOG(INFO) << "Stopping Profiler";
+-
+- ProfilerStop();
+-
+- http::OK response;
+- response.type = response.PATH;
+- response.path = "perftools.out";
+- response.headers["Content-Type"] = "application/octet-stream";
+- response.headers["Content-Disposition"] =
+- strings::format("attachment; filename=%s", PROFILE_FILE).get();
+-
+- started = false;
+- return response;
+-#else
+- return http::BadRequest(
+- "Perftools is disabled. To enable perftools, "
+- "configure libprocess with --enable-perftools.\n");
+-#endif
+- }
+-
+- bool started;
+-};
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_PROCESS_HPP__
+diff --git a/3rdparty/libprocess/include/process/protobuf.hpp b/3rdparty/libprocess/include/process/protobuf.hpp
+deleted file mode 100644
+index 5a6fc83..0000000
+--- a/3rdparty/libprocess/include/process/protobuf.hpp
++++ /dev/null
+@@ -1,737 +0,0 @@
+-#ifndef __PROCESS_PROTOBUF_HPP__
+-#define __PROCESS_PROTOBUF_HPP__
+-
+-#include <glog/logging.h>
+-
+-#include <google/protobuf/message.h>
+-#include <google/protobuf/repeated_field.h>
+-
+-#include <set>
+-#include <vector>
+-
+-#include <tr1/functional>
+-#include <tr1/unordered_map>
+-
+-#include <process/dispatch.hpp>
+-#include <process/process.hpp>
+-
+-#include <stout/lambda.hpp>
+-
+-
+-// Provides an implementation of process::post that for a protobuf.
+-namespace process {
+-
+-inline void post(const process::UPID& to,
+- const google::protobuf::Message& message)
+-{
+- std::string data;
+- message.SerializeToString(&data);
+- post(to, message.GetTypeName(), data.data(), data.size());
+-}
+-
+-
+-inline void post(const process::UPID& from,
+- const process::UPID& to,
+- const google::protobuf::Message& message)
+-{
+- std::string data;
+- message.SerializeToString(&data);
+- post(from, to, message.GetTypeName(), data.data(), data.size());
+-}
+-
+-} // namespace process {
+-
+-
+-// The rest of this file provides libprocess "support" for using
+-// protocol buffers. In particular, this file defines a subclass of
+-// Process (ProtobufProcess) that allows you to install protocol
+-// buffer handlers in addition to normal message and HTTP
+-// handlers. Install handlers can optionally take the sender's UPID
+-// as their first argument.
+-// Note that this header file assumes you will be linking
+-// against BOTH libprotobuf and libglog.
+-
+-namespace google { namespace protobuf {
+-
+-// Type conversions helpful for changing between protocol buffer types
+-// and standard C++ types (for parameters).
+-template <typename T>
+-const T& convert(const T& t)
+-{
+- return t;
+-}
+-
+-
+-template <typename T>
+-std::vector<T> convert(const google::protobuf::RepeatedPtrField<T>& items)
+-{
+- std::vector<T> result;
+- for (int i = 0; i < items.size(); i++) {
+- result.push_back(items.Get(i));
+- }
+-
+- return result;
+-}
+-
+-}} // namespace google { namespace protobuf {
+-
+-
+-template <typename T>
+-class ProtobufProcess : public process::Process<T>
+-{
+-public:
+- virtual ~ProtobufProcess() {}
+-
+-protected:
+- virtual void visit(const process::MessageEvent& event)
+- {
+- if (protobufHandlers.count(event.message->name) > 0) {
+- from = event.message->from; // For 'reply'.
+- protobufHandlers[event.message->name](
+- event.message->from, event.message->body);
+- from = process::UPID();
+- } else {
+- process::Process<T>::visit(event);
+- }
+- }
+-
+- void send(const process::UPID& to,
+- const google::protobuf::Message& message)
+- {
+- std::string data;
+- message.SerializeToString(&data);
+- process::Process<T>::send(to, message.GetTypeName(),
+- data.data(), data.size());
+- }
+-
+- using process::Process<T>::send;
+-
+- void reply(const google::protobuf::Message& message)
+- {
+- CHECK(from) << "Attempting to reply without a sender";
+- std::string data;
+- message.SerializeToString(&data);
+- send(from, message);
+- }
+-
+- // Installs that take the sender as the first argument.
+- template <typename M>
+- void install(void (T::*method)(const process::UPID&, const M&))
+- {
+- google::protobuf::Message* m = new M();
+- T* t = static_cast<T*>(this);
+- protobufHandlers[m->GetTypeName()] =
+- lambda::bind(&handlerM<M>,
+- t, method,
+- lambda::_1, lambda::_2);
+- delete m;
+- }
+-
+- template <typename M>
+- void install(void (T::*method)(const process::UPID&))
+- {
+- google::protobuf::Message* m = new M();
+- T* t = static_cast<T*>(this);
+- protobufHandlers[m->GetTypeName()] =
+- lambda::bind(&handler0,
+- t, method,
+- lambda::_1, lambda::_2);
+- delete m;
+- }
+-
+- template <typename M,
+- typename P1, typename P1C>
+- void install(
+- void (T::*method)(const process::UPID&, P1C),
+- P1 (M::*param1)() const)
+- {
+- google::protobuf::Message* m = new M();
+- T* t = static_cast<T*>(this);
+- protobufHandlers[m->GetTypeName()] =
+- lambda::bind(&handler1<M, P1, P1C>,
+- t, method, param1,
+- lambda::_1, lambda::_2);
+- delete m;
+- }
+-
+- template <typename M,
+- typename P1, typename P1C,
+- typename P2, typename P2C>
+- void install(
+- void (T::*method)(const process::UPID&, P1C, P2C),
+- P1 (M::*p1)() const,
+- P2 (M::*p2)() const)
+- {
+- google::protobuf::Message* m = new M();
+- T* t = static_cast<T*>(this);
+- protobufHandlers[m->GetTypeName()] =
+- lambda::bind(&handler2<M, P1, P1C, P2, P2C>,
+- t, method, p1, p2,
+- lambda::_1, lambda::_2);
+- delete m;
+- }
+-
+- template <typename M,
+- typename P1, typename P1C,
+- typename P2, typename P2C,
+- typename P3, typename P3C>
+- void install(
+- void (T::*method)(const process::UPID&, P1C, P2C, P3C),
+- P1 (M::*p1)() const,
+- P2 (M::*p2)() const,
+- P3 (M::*p3)() const)
+- {
+- google::protobuf::Message* m = new M();
+- T* t = static_cast<T*>(this);
+- protobufHandlers[m->GetTypeName()] =
+- lambda::bind(&handler3<M, P1, P1C, P2, P2C, P3, P3C>,
+- t, method, p1, p2, p3,
+- lambda::_1, lambda::_2);
+- delete m;
+- }
+-
+- template <typename M,
+- typename P1, typename P1C,
+- typename P2, typename P2C,
+- typename P3, typename P3C,
+- typename P4, typename P4C>
+- void install(
+- void (T::*method)(const process::UPID&, P1C, P2C, P3C, P4C),
+- P1 (M::*p1)() const,
+- P2 (M::*p2)() const,
+- P3 (M::*p3)() const,
+- P4 (M::*p4)() const)
+- {
+- google::protobuf::Message* m = new M();
+- T* t = static_cast<T*>(this);
+- protobufHandlers[m->GetTypeName()] =
+- lambda::bind(&handler4<M, P1, P1C, P2, P2C, P3, P3C, P4, P4C>,
+- t, method, p1, p2, p3, p4,
+- lambda::_1, lambda::_2);
+- delete m;
+- }
+-
+- template <typename M,
+- typename P1, typename P1C,
+- typename P2, typename P2C,
+- typename P3, typename P3C,
+- typename P4, typename P4C,
+- typename P5, typename P5C>
+- void install(
+- void (T::*method)(const process::UPID&, P1C, P2C, P3C, P4C, P5C),
+- P1 (M::*p1)() const,
+- P2 (M::*p2)() const,
+- P3 (M::*p3)() const,
+- P4 (M::*p4)() const,
+- P5 (M::*p5)() const)
+- {
+- google::protobuf::Message* m = new M();
+- T* t = static_cast<T*>(this);
+- protobufHandlers[m->GetTypeName()] =
+- lambda::bind(&handler5<M, P1, P1C, P2, P2C, P3, P3C, P4, P4C, P5, P5C>,
+- t, method, p1, p2, p3, p4, p5,
+- lambda::_1, lambda::_2);
+- delete m;
+- }
+-
+- // Installs that do not take the sender.
+- template <typename M>
+- void install(void (T::*method)(const M&))
+- {
+- google::protobuf::Message* m = new M();
+- T* t = static_cast<T*>(this);
+- protobufHandlers[m->GetTypeName()] =
+- lambda::bind(&_handlerM<M>,
+- t, method,
+- lambda::_1, lambda::_2);
+- delete m;
+- }
+-
+- template <typename M>
+- void install(void (T::*method)())
+- {
+- google::protobuf::Message* m = new M();
+- T* t = static_cast<T*>(this);
+- protobufHandlers[m->GetTypeName()] =
+- lambda::bind(&_handler0,
+- t, method,
+- lambda::_1, lambda::_2);
+- delete m;
+- }
+-
+- template <typename M,
+- typename P1, typename P1C>
+- void install(
+- void (T::*method)(P1C),
+- P1 (M::*param1)() const)
+- {
+- google::protobuf::Message* m = new M();
+- T* t = static_cast<T*>(this);
+- protobufHandlers[m->GetTypeName()] =
+- lambda::bind(&_handler1<M, P1, P1C>,
+- t, method, param1,
+- lambda::_1, lambda::_2);
+- delete m;
+- }
+-
+- template <typename M,
+- typename P1, typename P1C,
+- typename P2, typename P2C>
+- void install(
+- void (T::*method)(P1C, P2C),
+- P1 (M::*p1)() const,
+- P2 (M::*p2)() const)
+- {
+- google::protobuf::Message* m = new M();
+- T* t = static_cast<T*>(this);
+- protobufHandlers[m->GetTypeName()] =
+- lambda::bind(&_handler2<M, P1, P1C, P2, P2C>,
+- t, method, p1, p2,
+- lambda::_1, lambda::_2);
+- delete m;
+- }
+-
+- template <typename M,
+- typename P1, typename P1C,
+- typename P2, typename P2C,
+- typename P3, typename P3C>
+- void install(
+- void (T::*method)(P1C, P2C, P3C),
+- P1 (M::*p1)() const,
+- P2 (M::*p2)() const,
+- P3 (M::*p3)() const)
+- {
+- google::protobuf::Message* m = new M();
+- T* t = static_cast<T*>(this);
+- protobufHandlers[m->GetTypeName()] =
+- lambda::bind(&_handler3<M, P1, P1C, P2, P2C, P3, P3C>,
+- t, method, p1, p2, p3,
+- lambda::_1, lambda::_2);
+- delete m;
+- }
+-
+- template <typename M,
+- typename P1, typename P1C,
+- typename P2, typename P2C,
+- typename P3, typename P3C,
+- typename P4, typename P4C>
+- void install(
+- void (T::*method)(P1C, P2C, P3C, P4C),
+- P1 (M::*p1)() const,
+- P2 (M::*p2)() const,
+- P3 (M::*p3)() const,
+- P4 (M::*p4)() const)
+- {
+- google::protobuf::Message* m = new M();
+- T* t = static_cast<T*>(this);
+- protobufHandlers[m->GetTypeName()] =
+- lambda::bind(&_handler4<M, P1, P1C, P2, P2C, P3, P3C, P4, P4C>,
+- t, method, p1, p2, p3, p4,
+- lambda::_1, lambda::_2);
+- delete m;
+- }
+-
+- template <typename M,
+- typename P1, typename P1C,
+- typename P2, typename P2C,
+- typename P3, typename P3C,
+- typename P4, typename P4C,
+- typename P5, typename P5C>
+- void install(
+- void (T::*method)(P1C, P2C, P3C, P4C, P5C),
+- P1 (M::*p1)() const,
+- P2 (M::*p2)() const,
+- P3 (M::*p3)() const,
+- P4 (M::*p4)() const,
+- P5 (M::*p5)() const)
+- {
+- google::protobuf::Message* m = new M();
+- T* t = static_cast<T*>(this);
+- protobufHandlers[m->GetTypeName()] =
+- lambda::bind(&_handler5<M, P1, P1C, P2, P2C, P3, P3C, P4, P4C, P5, P5C>,
+- t, method, p1, p2, p3, p4, p5,
+- lambda::_1, lambda::_2);
+- delete m;
+- }
+-
+- using process::Process<T>::install;
+-
+-private:
+- // Handlers that take the sender as the first argument.
+- template <typename M>
+- static void handlerM(
+- T* t,
+- void (T::*method)(const process::UPID&, const M&),
+- const process::UPID& sender,
+- const std::string& data)
+- {
+- M m;
+- m.ParseFromString(data);
+- if (m.IsInitialized()) {
+- (t->*method)(sender, m);
+- } else {
+- LOG(WARNING) << "Initialization errors: "
+- << m.InitializationErrorString();
+- }
+- }
+-
+- static void handler0(
+- T* t,
+- void (T::*method)(const process::UPID&),
+- const process::UPID& sender,
+- const std::string& data)
+- {
+- (t->*method)(sender);
+- }
+-
+- template <typename M,
+- typename P1, typename P1C>
+- static void handler1(
+- T* t,
+- void (T::*method)(const process::UPID&, P1C),
+- P1 (M::*p1)() const,
+- const process::UPID& sender,
+- const std::string& data)
+- {
+- M m;
+- m.ParseFromString(data);
+- if (m.IsInitialized()) {
+- (t->*method)(sender, google::protobuf::convert((&m->*p1)()));
+- } else {
+- LOG(WARNING) << "Initialization errors: "
+- << m.InitializationErrorString();
+- }
+- }
+-
+- template <typename M,
+- typename P1, typename P1C,
+- typename P2, typename P2C>
+- static void handler2(
+- T* t,
+- void (T::*method)(const process::UPID&, P1C, P2C),
+- P1 (M::*p1)() const,
+- P2 (M::*p2)() const,
+- const process::UPID& sender,
+- const std::string& data)
+- {
+- M m;
+- m.ParseFromString(data);
+- if (m.IsInitialized()) {
+- (t->*method)(sender,
+- google::protobuf::convert((&m->*p1)()),
+- google::protobuf::convert((&m->*p2)()));
+- } else {
+- LOG(WARNING) << "Initialization errors: "
+- << m.InitializationErrorString();
+- }
+- }
+-
+- template <typename M,
+- typename P1, typename P1C,
+- typename P2, typename P2C,
+- typename P3, typename P3C>
+- static void handler3(
+- T* t,
+- void (T::*method)(const process::UPID&, P1C, P2C, P3C),
+- P1 (M::*p1)() const,
+- P2 (M::*p2)() const,
+- P3 (M::*p3)() const,
+- const process::UPID& sender,
+- const std::string& data)
+- {
+- M m;
+- m.ParseFromString(data);
+- if (m.IsInitialized()) {
+- (t->*method)(sender,
+- google::protobuf::convert((&m->*p1)()),
+- google::protobuf::convert((&m->*p2)()),
+- google::protobuf::convert((&m->*p3)()));
+- } else {
+- LOG(WARNING) << "Initialization errors: "
+- << m.InitializationErrorString();
+- }
+- }
+-
+- template <typename M,
+- typename P1, typename P1C,
+- typename P2, typename P2C,
+- typename P3, typename P3C,
+- typename P4, typename P4C>
+- static void handler4(
+- T* t,
+- void (T::*method)(const process::UPID&, P1C, P2C, P3C, P4C),
+- P1 (M::*p1)() const,
+- P2 (M::*p2)() const,
+- P3 (M::*p3)() const,
+- P4 (M::*p4)() const,
+- const process::UPID& sender,
+- const std::string& data)
+- {
+- M m;
+- m.ParseFromString(data);
+- if (m.IsInitialized()) {
+- (t->*method)(sender,
+- google::protobuf::convert((&m->*p1)()),
+- google::protobuf::convert((&m->*p2)()),
+- google::protobuf::convert((&m->*p3)()),
+- google::protobuf::convert((&m->*p4)()));
+- } else {
+- LOG(WARNING) << "Initialization errors: "
+- << m.InitializationErrorString();
+- }
+- }
+-
+- template <typename M,
+- typename P1, typename P1C,
+- typename P2, typename P2C,
+- typename P3, typename P3C,
+- typename P4, typename P4C,
+- typename P5, typename P5C>
+- static void handler5(
+- T* t,
+- void (T::*method)(const process::UPID&, P1C, P2C, P3C, P4C, P5C),
+- P1 (M::*p1)() const,
+- P2 (M::*p2)() const,
+- P3 (M::*p3)() const,
+- P4 (M::*p4)() const,
+- P5 (M::*p5)() const,
+- const process::UPID& sender,
+- const std::string& data)
+- {
+- M m;
+- m.ParseFromString(data);
+- if (m.IsInitialized()) {
+- (t->*method)(sender,
+- google::protobuf::convert((&m->*p1)()),
+- google::protobuf::convert((&m->*p2)()),
+- google::protobuf::convert((&m->*p3)()),
+- google::protobuf::convert((&m->*p4)()),
+- google::protobuf::convert((&m->*p5)()));
+- } else {
+- LOG(WARNING) << "Initialization errors: "
+- << m.InitializationErrorString();
+- }
+- }
+-
+- // Handlers that ignore the sender.
+- template <typename M>
+- static void _handlerM(
+- T* t,
+- void (T::*method)(const M&),
+- const process::UPID&,
+- const std::string& data)
+- {
+- M m;
+- m.ParseFromString(data);
+- if (m.IsInitialized()) {
+- (t->*method)(m);
+- } else {
+- LOG(WARNING) << "Initialization errors: "
+- << m.InitializationErrorString();
+- }
+- }
+-
+- static void _handler0(
+- T* t,
+- void (T::*method)(),
+- const process::UPID&,
+- const std::string& data)
+- {
+- (t->*method)();
+- }
+-
+- template <typename M,
+- typename P1, typename P1C>
+- static void _handler1(
+- T* t,
+- void (T::*method)(P1C),
+- P1 (M::*p1)() const,
+- const process::UPID&,
+- const std::string& data)
+- {
+- M m;
+- m.ParseFromString(data);
+- if (m.IsInitialized()) {
+- (t->*method)(google::protobuf::convert((&m->*p1)()));
+- } else {
+- LOG(WARNING) << "Initialization errors: "
+- << m.InitializationErrorString();
+- }
+- }
+-
+- template <typename M,
+- typename P1, typename P1C,
+- typename P2, typename P2C>
+- static void _handler2(
+- T* t,
+- void (T::*method)(P1C, P2C),
+- P1 (M::*p1)() const,
+- P2 (M::*p2)() const,
+- const process::UPID&,
+- const std::string& data)
+- {
+- M m;
+- m.ParseFromString(data);
+- if (m.IsInitialized()) {
+- (t->*method)(google::protobuf::convert((&m->*p1)()),
+- google::protobuf::convert((&m->*p2)()));
+- } else {
+- LOG(WARNING) << "Initialization errors: "
+- << m.InitializationErrorString();
+- }
+- }
+-
+- template <typename M,
+- typename P1, typename P1C,
+- typename P2, typename P2C,
+- typename P3, typename P3C>
+- static void _handler3(
+- T* t,
+- void (T::*method)(P1C, P2C, P3C),
+- P1 (M::*p1)() const,
+- P2 (M::*p2)() const,
+- P3 (M::*p3)() const,
+- const process::UPID&,
+- const std::string& data)
+- {
+- M m;
+- m.ParseFromString(data);
+- if (m.IsInitialized()) {
+- (t->*method)(google::protobuf::convert((&m->*p1)()),
+- google::protobuf::convert((&m->*p2)()),
+- google::protobuf::convert((&m->*p3)()));
+- } else {
+- LOG(WARNING) << "Initialization errors: "
+- << m.InitializationErrorString();
+- }
+- }
+-
+- template <typename M,
+- typename P1, typename P1C,
+- typename P2, typename P2C,
+- typename P3, typename P3C,
+- typename P4, typename P4C>
+- static void _handler4(
+- T* t,
+- void (T::*method)(P1C, P2C, P3C, P4C),
+- P1 (M::*p1)() const,
+- P2 (M::*p2)() const,
+- P3 (M::*p3)() const,
+- P4 (M::*p4)() const,
+- const process::UPID&,
+- const std::string& data)
+- {
+- M m;
+- m.ParseFromString(data);
+- if (m.IsInitialized()) {
+- (t->*method)(google::protobuf::convert((&m->*p1)()),
+- google::protobuf::convert((&m->*p2)()),
+- google::protobuf::convert((&m->*p3)()),
+- google::protobuf::convert((&m->*p4)()));
+- } else {
+- LOG(WARNING) << "Initialization errors: "
+- << m.InitializationErrorString();
+- }
+- }
+-
+- template <typename M,
+- typename P1, typename P1C,
+- typename P2, typename P2C,
+- typename P3, typename P3C,
+- typename P4, typename P4C,
+- typename P5, typename P5C>
+- static void _handler5(
+- T* t,
+- void (T::*method)(P1C, P2C, P3C, P4C, P5C),
+- P1 (M::*p1)() const,
+- P2 (M::*p2)() const,
+- P3 (M::*p3)() const,
+- P4 (M::*p4)() const,
+- P5 (M::*p5)() const,
+- const process::UPID&,
+- const std::string& data)
+- {
+- M m;
+- m.ParseFromString(data);
+- if (m.IsInitialized()) {
+- (t->*method)(google::protobuf::convert((&m->*p1)()),
+- google::protobuf::convert((&m->*p2)()),
+- google::protobuf::convert((&m->*p3)()),
+- google::protobuf::convert((&m->*p4)()),
+- google::protobuf::convert((&m->*p5)()));
+- } else {
+- LOG(WARNING) << "Initialization errors: "
+- << m.InitializationErrorString();
+- }
+- }
+-
+- typedef lambda::function<
+- void(const process::UPID&, const std::string&)> handler;
+- std::tr1::unordered_map<std::string, handler> protobufHandlers;
+-
+- // Sender of "current" message, inaccessible by subclasses.
+- // This is only used for reply().
+- process::UPID from;
+-};
+-
+-
+-// Implements a process for sending protobuf "requests" to a process
+-// and waiting for a protobuf "response", but uses futures so that
+-// this can be done without needing to implement a process.
+-template <typename Req, typename Res>
+-class ReqResProcess : public ProtobufProcess<ReqResProcess<Req, Res> >
+-{
+-public:
+- ReqResProcess(const process::UPID& _pid, const Req& _req)
+- : pid(_pid), req(_req)
+- {
+- ProtobufProcess<ReqResProcess<Req, Res> >::template
+- install<Res>(&ReqResProcess<Req, Res>::response);
+- }
+-
+- process::Future<Res> run()
+- {
+- // Terminate this process if no one cares about the response
+- // (note, we need to disambiguate the process::terminate).
+- void (*terminate)(const process::UPID&, bool) = &process::terminate;
+- promise.future().onDiscarded(
+- lambda::bind(terminate, process::ProcessBase::self(), true));
+-
+- ProtobufProcess<ReqResProcess<Req, Res> >::send(pid, req);
+-
+- return promise.future();
+- }
+-
+-private:
+- void response(const Res& res)
+- {
+- promise.set(res);
+- process::terminate(process::ProcessBase::self());
+- }
+-
+- const process::UPID pid;
+- const Req req;
+- process::Promise<Res> promise;
+-};
+-
+-
+-// Allows you to describe request/response protocols and then use
+-// those for sending requests and getting back responses.
+-template <typename Req, typename Res>
+-struct Protocol
+-{
+- process::Future<Res> operator () (
+- const process::UPID& pid,
+- const Req& req) const
+- {
+- // Help debugging by adding some "type constraints".
+- { Req* req = NULL; google::protobuf::Message* m = req; (void)m; }
+- { Res* res = NULL; google::protobuf::Message* m = res; (void)m; }
+-
+- ReqResProcess<Req, Res>* process = new ReqResProcess<Req, Res>(pid, req);
+- process::spawn(process, true);
+- return process::dispatch(process, &ReqResProcess<Req, Res>::run);
+- }
+-};
+-
+-#endif // __PROCESS_PROTOBUF_HPP__
+diff --git a/3rdparty/libprocess/include/process/run.hpp b/3rdparty/libprocess/include/process/run.hpp
+deleted file mode 100644
+index a245b70..0000000
+--- a/3rdparty/libprocess/include/process/run.hpp
++++ /dev/null
+@@ -1,80 +0,0 @@
+-#ifndef __PROCESS_RUN_HPP__
+-#define __PROCESS_RUN_HPP__
+-
+-#include <tr1/memory> // TODO(benh): Replace shared_ptr with unique_ptr.
+-
+-#include <process/process.hpp>
+-
+-#include <stout/preprocessor.hpp>
+-
+-namespace process {
+-
+-namespace internal {
+-
+-template <typename R>
+-class ThunkProcess : public Process<ThunkProcess<R> >
+-{
+-public:
+- ThunkProcess(std::tr1::shared_ptr<std::tr1::function<R(void)> > _thunk,
+- std::tr1::shared_ptr<Promise<R> > _promise)
+- : thunk(_thunk),
+- promise(_promise) {}
+-
+- virtual ~ThunkProcess() {}
+-
+-protected:
+- virtual void serve(const Event& event)
+- {
+- promise->set((*thunk)());
+- }
+-
+-private:
+- std::tr1::shared_ptr<std::tr1::function<R(void)> > thunk;
+- std::tr1::shared_ptr<Promise<R> > promise;
+-};
+-
+-} // namespace internal {
+-
+-
+-template <typename R>
+-Future<R> run(R (*method)(void))
+-{
+- std::tr1::shared_ptr<std::tr1::function<R(void)> > thunk(
+- new std::tr1::function<R(void)>(
+- std::tr1::bind(method)));
+-
+- std::tr1::shared_ptr<Promise<R> > promise(new Promise<R>());
+- Future<R> future = promise->future();
+-
+- terminate(spawn(new internal::ThunkProcess<R>(thunk, promise), true));
+-
+- return future;
+-}
+-
+-
+-#define TEMPLATE(Z, N, DATA) \
+- template <typename R, \
+- ENUM_PARAMS(N, typename P), \
+- ENUM_PARAMS(N, typename A)> \
+- Future<R> run( \
+- R (*method)(ENUM_PARAMS(N, P)), \
+- ENUM_BINARY_PARAMS(N, A, a)) \
+- { \
+- std::tr1::shared_ptr<std::tr1::function<R(void)> > thunk( \
+- new std::tr1::function<R(void)>( \
+- std::tr1::bind(method, ENUM_PARAMS(N, a)))); \
+- \
+- std::tr1::shared_ptr<Promise<R> > promise(new Promise<R>()); \
+- Future<R> future = promise->future(); \
+- \
+- terminate(spawn(new internal::ThunkProcess<R>(thunk, promise), true)); \
+- \
+- return future; \
+- }
+-
+- REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
+-#undef TEMPLATE
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_RUN_HPP__
+diff --git a/3rdparty/libprocess/include/process/shared.hpp b/3rdparty/libprocess/include/process/shared.hpp
+deleted file mode 100644
+index 8f5b59b..0000000
+--- a/3rdparty/libprocess/include/process/shared.hpp
++++ /dev/null
+@@ -1,183 +0,0 @@
+-#ifndef __PROCESS_SHARED_HPP__
+-#define __PROCESS_SHARED_HPP__
+-
+-#include <glog/logging.h>
+-
+-#include <boost/shared_ptr.hpp>
+-
+-#include <process/future.hpp>
+-
+-namespace process {
+-
+-// Forward declaration.
+-template <typename T>
+-class Owned;
+-
+-
+-// Represents a shared pointer and therefore enforces 'const' access.
+-template <typename T>
+-class Shared
+-{
+-public:
+- Shared();
+- explicit Shared(T* t);
+-
+- bool operator == (const Shared<T>& that) const;
+- bool operator < (const Shared<T>& that) const;
+-
+- // Enforces const access semantics.
+- const T& operator * () const;
+- const T* operator -> () const;
+- const T* get() const;
+-
+- bool unique() const;
+-
+- void reset();
+- void reset(T* t);
+- void swap(Shared<T>& that);
+-
+- // Transfers ownership of the pointer by waiting for exclusive
+- // access (i.e., no other Shared instances). This shared pointer
+- // will be reset after this function is invoked. If multiple shared
+- // pointers pointing to the same object all want to be upgraded,
+- // only one of them may succeed and the rest will get failures.
+- Future<Owned<T> > own();
+-
+-private:
+- struct Data
+- {
+- Data(T* _t);
+- ~Data();
+-
+- T* t;
+- volatile bool owned;
+- Promise<Owned<T> > promise;
+- };
+-
+- boost::shared_ptr<Data> data;
+-};
+-
+-
+-template <typename T>
+-Shared<T>::Shared() {}
+-
+-
+-template <typename T>
+-Shared<T>::Shared(T* t)
+-{
+- if (t != NULL) {
+- data.reset(new Data(t));
+- }
+-}
+-
+-
+-template <typename T>
+-bool Shared<T>::operator == (const Shared<T>& that) const
+-{
+- return data == that.data;
+-}
+-
+-
+-template <typename T>
+-bool Shared<T>::operator < (const Shared<T>& that) const
+-{
+- return data < that.data;
+-}
+-
+-
+-template <typename T>
+-const T& Shared<T>::operator * () const
+-{
+- return *CHECK_NOTNULL(get());
+-}
+-
+-
+-template <typename T>
+-const T* Shared<T>::operator -> () const
+-{
+- return CHECK_NOTNULL(get());
+-}
+-
+-
+-template <typename T>
+-const T* Shared<T>::get() const
+-{
+- if (data.get() == NULL) {
+- return NULL;
+- } else {
+- return data->t;
+- }
+-}
+-
+-
+-template <typename T>
+-bool Shared<T>::unique() const
+-{
+- return data.unique();
+-}
+-
+-
+-template <typename T>
+-void Shared<T>::reset()
+-{
+- data.reset();
+-}
+-
+-
+-template <typename T>
+-void Shared<T>::reset(T* t)
+-{
+- if (t == NULL) {
+- data.reset();
+- } else {
+- data.reset(new Data(t));
+- }
+-}
+-
+-
+-template <typename T>
+-void Shared<T>::swap(Shared<T>& that)
+-{
+- data.swap(that.data);
+-}
+-
+-
+-template <typename T>
+-Future<Owned<T> > Shared<T>::own()
+-{
+- // If two threads simultaneously access this object and at least one
+- // of them is a write, the behavior is undefined. This is similar to
+- // boost::shared_ptr. For more details, please refer to the boost
+- // shared_ptr document (section "Thread Safety").
+- if (data.get() == NULL) {
+- return Owned<T>(NULL);
+- }
+-
+- if (!__sync_bool_compare_and_swap(&data->owned, false, true)) {
+- return Failure("Ownership has already been transferred");
+- }
+-
+- Future<Owned<T> > future = data->promise.future();
+- data.reset();
+- return future;
+-}
+-
+-
+-template <typename T>
+-Shared<T>::Data::Data(T* _t)
+- : t(CHECK_NOTNULL(_t)), owned(false) {}
+-
+-
+-template <typename T>
+-Shared<T>::Data::~Data()
+-{
+- if (owned) {
+- promise.set(Owned<T>(t));
+- } else {
+- delete t;
+- }
+-}
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_SHARED_HPP__
+diff --git a/3rdparty/libprocess/include/process/socket.hpp b/3rdparty/libprocess/include/process/socket.hpp
+deleted file mode 100644
+index 669a333..0000000
+--- a/3rdparty/libprocess/include/process/socket.hpp
++++ /dev/null
+@@ -1,84 +0,0 @@
+-#ifndef __PROCESS_SOCKET_HPP__
+-#define __PROCESS_SOCKET_HPP__
+-
+-#include <assert.h>
+-#include <unistd.h> // For close.
+-
+-#include <iostream>
+-
+-#include <stout/nothing.hpp>
+-#include <stout/os.hpp>
+-#include <stout/try.hpp>
+-
+-// An abstraction around a socket (file descriptor) that provides
+-// reference counting such that the socket is only closed (and thus,
+-// has the possiblity of being reused) after there are no more
+-// references.
+-
+-class Socket
+-{
+-public:
+- Socket()
+- : refs(new int(1)), s(-1) {}
+-
+- explicit Socket(int _s)
+- : refs(new int(1)), s(_s) {}
+-
+- ~Socket()
+- {
+- cleanup();
+- }
+-
+- Socket(const Socket& that)
+- {
+- copy(that);
+- }
+-
+- Socket& operator = (const Socket& that)
+- {
+- if (this != &that) {
+- cleanup();
+- copy(that);
+- }
+- return *this;
+- }
+-
+- bool operator == (const Socket& that) const
+- {
+- return s == that.s && refs == that.refs;
+- }
+-
+- operator int () const
+- {
+- return s;
+- }
+-
+-private:
+- void copy(const Socket& that)
+- {
+- assert(that.refs > 0);
+- __sync_fetch_and_add(that.refs, 1);
+- refs = that.refs;
+- s = that.s;
+- }
+-
+- void cleanup()
+- {
+- assert(refs != NULL);
+- if (__sync_sub_and_fetch(refs, 1) == 0) {
+- delete refs;
+- if (s >= 0) {
+- Try<Nothing> close = os::close(s);
+- if (close.isError()) {
+- std::cerr << "Failed to close socket: " << close.error() << std::endl;
+- abort();
+- }
+- }
+- }
+- }
+-
+- int* refs;
+- int s;
+-};
+-
+-#endif // __PROCESS_SOCKET_HPP__
+diff --git a/3rdparty/libprocess/include/process/statistics.hpp b/3rdparty/libprocess/include/process/statistics.hpp
+deleted file mode 100644
+index ce122a5..0000000
+--- a/3rdparty/libprocess/include/process/statistics.hpp
++++ /dev/null
+@@ -1,160 +0,0 @@
+-#ifndef __PROCESS_STATISTICS_HPP__
+-#define __PROCESS_STATISTICS_HPP__
+-
+-#include <process/clock.hpp>
+-#include <process/future.hpp>
+-#include <process/owned.hpp>
+-#include <process/time.hpp>
+-
+-#include <stout/duration.hpp>
+-#include <stout/none.hpp>
+-#include <stout/nothing.hpp>
+-#include <stout/option.hpp>
+-
+-namespace process {
+-
+-// Forward declarations.
+-class Statistics;
+-class StatisticsProcess;
+-
+-namespace meters {
+- class Meter;
+- class TimeRate;
+-}
+-
+-
+-// Libprocess statistics handle.
+-// To be used from anywhere to manage statistics.
+-//
+-// Ex: process::statistics->increment("http", "num_requests");
+-// process::statistics->set("http", "response_size", response.size());
+-//
+-// Statistics are exposed via JSON for external visibility.
+-extern Statistics* statistics;
+-
+-const Duration STATISTICS_TRUNCATION_INTERVAL = Minutes(5);
+-
+-// Provides an in-memory time series of statistics over some window
+-// (values are truncated outside of the window, but no limit is
+-// currently placed on the number of values within a window).
+-//
+-// TODO(bmahler): Time series granularity should be coarsened over
+-// time. This means, for high-frequency statistics, we keep a lot of
+-// recent data points (fine granularity), and keep fewer older data
+-// points (coarse granularity). The tunable bit here could be the
+-// total number of data points to keep around, which informs how
+-// often to delete older data points, while still keeping a window
+-// worth of data.
+-class Statistics
+-{
+-public:
+- Statistics(const Duration& window);
+- ~Statistics();
+-
+- // Returns the time series of a statistic.
+- process::Future<std::map<Time, double> > timeseries(
+- const std::string& context,
+- const std::string& name,
+- const Option<Time>& start = None(),
+- const Option<Time>& stop = None());
+-
+- // Returns the latest value of a statistic.
+- process::Future<Option<double> > get(
+- const std::string& context,
+- const std::string& name);
+-
+- // Returns the latest values of all statistics in the context.
+- process::Future<std::map<std::string, double> > get(
+- const std::string& context);
+-
+- // Adds a meter for the statistic with the provided context and name.
+- // get(context, meter->name) will return the metered time series.
+- // Returns an error if:
+- // -meter->name == name, or
+- // -The meter already exists.
+- Future<Try<Nothing> > meter(
+- const std::string& context,
+- const std::string& name,
+- Owned<meters::Meter> meter);
+-
+- // Sets the current value of a statistic at the current clock time
+- // or at a specified time.
+- void set(
+- const std::string& context,
+- const std::string& name,
+- double value,
+- const Time& time = Clock::now());
+-
+- // Archives the provided statistic time series, and any meters associated
+- // with it. This means three things:
+- // 1. The statistic will no longer be part of the snapshot.
+- // 2. However, the time series will be retained until the window expiration.
+- // 3. All meters associated with this statistic will be removed, both
+- // (1) and (2) will apply to the metered time series as well.
+- void archive(const std::string& context, const std::string& name);
+-
+- // Increments the current value of a statistic. If no statistic was
+- // previously present, an initial value of 0.0 is used.
+- void increment(const std::string& context, const std::string& name);
+-
+- // Decrements the current value of a statistic. If no statistic was
+- // previously present, an initial value of 0.0 is used.
+- void decrement(const std::string& context, const std::string& name);
+-
+-private:
+- StatisticsProcess* process;
+-};
+-
+-
+-namespace meters {
+-
+-// This is the interface for statistical meters.
+-// Meters provide additional metering on top of the raw statistical
+-// value. Ex: Track the maximum, average, rate, etc.
+-class Meter
+-{
+-protected:
+- Meter(const std::string& _name) : name(_name) {}
+-
+-public:
+- virtual ~Meter() {}
+-
+- // Updates the meter with another input value.
+- // Returns the new metered value, or none if no metered value can be produced.
+- virtual Option<double> update(const Time& time, double value) = 0;
+-
+- const std::string name;
+-};
+-
+-
+-// Tracks the percent of time 'used' since the last update.
+-// Input values to this meter must be in seconds.
+-class TimeRate : public Meter
+-{
+-public:
+- TimeRate(const std::string& name)
+- : Meter(name), time(None()), value(0) {}
+-
+- virtual ~TimeRate() {}
+-
+- virtual Option<double> update(const Time& _time, double _value)
+- {
+- Option<double> rate;
+- if (time.isSome()) {
+- rate = (_value - value) / (_time - time.get()).secs();
+- }
+-
+- time = _time;
+- value = _value;
+- return rate;
+- }
+-
+-private:
+- Option<Time> time;
+- double value;
+-};
+-
+-} // namespace meters {
+-} // namespace process {
+-
+-#endif // __PROCESS_STATISTICS_HPP__
+diff --git a/3rdparty/libprocess/include/process/time.hpp b/3rdparty/libprocess/include/process/time.hpp
+deleted file mode 100644
+index 307fd2c..0000000
+--- a/3rdparty/libprocess/include/process/time.hpp
++++ /dev/null
+@@ -1,124 +0,0 @@
+-#ifndef __PROCESS_TIME_HPP__
+-#define __PROCESS_TIME_HPP__
+-
+-#include <iomanip>
+-
+-#include <glog/logging.h>
+-
+-#include <stout/duration.hpp>
+-
+-namespace process {
+-
+-// Represents an instant in time.
+-class Time
+-{
+-public:
+- // Constructs a time at the Epoch. It is needed because collections
+- // (e.g., std::map) require a default constructor to construct
+- // empty values.
+- Time() : sinceEpoch(Duration::zero()) {}
+-
+- static Time EPOCH;
+- static Time MAX;
+-
+- static Try<Time> create(double secs)
+- {
+- Try<Duration> duration = Duration::create(secs);
+- if (duration.isSome()) {
+- return Time(duration.get());
+- } else {
+- return Error("Argument too large for Time: " + duration.error());
+- }
+- }
+-
+- Duration duration() const { return sinceEpoch; }
+-
+- double secs() const { return sinceEpoch.secs(); }
+-
+- bool operator < (const Time& t) const { return sinceEpoch < t.sinceEpoch; }
+- bool operator <= (const Time& t) const { return sinceEpoch <= t.sinceEpoch; }
+- bool operator > (const Time& t) const { return sinceEpoch > t.sinceEpoch; }
+- bool operator >= (const Time& t) const { return sinceEpoch >= t.sinceEpoch; }
+- bool operator == (const Time& t) const { return sinceEpoch == t.sinceEpoch; }
+- bool operator != (const Time& t) const { return sinceEpoch != t.sinceEpoch; }
+-
+- Time& operator += (const Duration& d)
+- {
+- sinceEpoch += d;
+- return *this;
+- }
+-
+- Time& operator -= (const Duration& d)
+- {
+- sinceEpoch -= d;
+- return *this;
+- }
+-
+- Duration operator - (const Time& that) const
+- {
+- return sinceEpoch - that.sinceEpoch;
+- }
+-
+- Time operator + (const Duration& duration) const
+- {
+- Time new_ = *this;
+- new_ += duration;
+- return new_;
+- }
+-
+- Time operator - (const Duration& duration) const
+- {
+- Time new_ = *this;
+- new_ -= duration;
+- return new_;
+- }
+-
+-private:
+- Duration sinceEpoch;
+-
+- // Made it private to avoid the confusion between Time and Duration.
+- // Users should explicitly use Clock::now() and Time::create() to
+- // create a new time instance.
+- Time(const Duration& _sinceEpoch) : sinceEpoch(_sinceEpoch) {}
+-};
+-
+-
+-// Outputs the time in RFC 3339 Format.
+-inline std::ostream& operator << (std::ostream& stream, const Time& time)
+-{
+- // Round down the secs to use it with strftime and then append the
+- // fraction part.
+- long secs = static_cast<long>(time.secs());
+- char date[64];
+-
+- // The RFC 3339 Format.
+- tm* tm_ = gmtime(&secs);
+- if (tm_ == NULL) {
+- LOG(ERROR) << "Cannot convert the 'time' to a tm struct using gmtime(): "
+- << errno;
+- return stream;
+- }
+-
+- strftime(date, 64, "%Y-%m-%d %H:%M:%S", tm_);
+- stream << date;
+-
+- // Append the fraction part in nanoseconds.
+- int64_t nsecs = (time.duration() - Seconds(secs)).ns();
+-
+- if (nsecs != 0) {
+- char prev = stream.fill();
+-
+- // 9 digits for nanosecond level precision.
+- stream << "." << std::setfill('0') << std::setw(9) << nsecs;
+-
+- // Return the stream to original formatting state.
+- stream.fill(prev);
+- }
+-
+- stream << "+00:00";
+- return stream;
+-}
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_TIME_HPP__
+diff --git a/3rdparty/libprocess/include/process/timeout.hpp b/3rdparty/libprocess/include/process/timeout.hpp
+deleted file mode 100644
+index 4634b9f..0000000
+--- a/3rdparty/libprocess/include/process/timeout.hpp
++++ /dev/null
+@@ -1,84 +0,0 @@
+-#ifndef __PROCESS_TIMEOUT_HPP__
+-#define __PROCESS_TIMEOUT_HPP__
+-
+-#include <process/process.hpp>
+-
+-#include <process/time.hpp>
+-
+-#include <stout/duration.hpp>
+-
+-
+-namespace process {
+-
+-class Timeout
+-{
+-public:
+- Timeout() : timeout(Clock::now()) {}
+-
+- Timeout(const Time& time) : timeout(time) {}
+-
+- Timeout(const Timeout& that) : timeout(that.timeout) {}
+-
+- // Constructs a Timeout instance from a Time that is the 'duration'
+- // from now.
+- static Timeout in(const Duration& duration)
+- {
+- return Timeout(Clock::now() + duration);
+- }
+-
+- Timeout& operator = (const Timeout& that)
+- {
+- if (this != &that) {
+- timeout = that.timeout;
+- }
+-
+- return *this;
+- }
+-
+- Timeout& operator = (const Duration& duration)
+- {
+- timeout = Clock::now() + duration;
+- return *this;
+- }
+-
+- bool operator == (const Timeout& that) const
+- {
+- return timeout == that.timeout;
+- }
+-
+- bool operator < (const Timeout& that) const
+- {
+- return timeout < that.timeout;
+- }
+-
+- bool operator <= (const Timeout& that) const
+- {
+- return timeout <= that.timeout;
+- }
+-
+- // Returns the value of the timeout as a Time object.
+- Time time() const
+- {
+- return timeout;
+- }
+-
+- // Returns the amount of time remaining.
+- Duration remaining() const
+- {
+- Duration remaining = timeout - Clock::now();
+- return remaining > Duration::zero() ? remaining : Duration::zero();
+- }
+-
+- // Returns true if the timeout expired.
+- bool expired() const
+- {
+- return timeout <= Clock::now();
+- }
+-
+-private:
+- Time timeout;
+-};
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_TIMEOUT_HPP__
+diff --git a/3rdparty/libprocess/include/process/timer.hpp b/3rdparty/libprocess/include/process/timer.hpp
+deleted file mode 100644
+index 333a806..0000000
+--- a/3rdparty/libprocess/include/process/timer.hpp
++++ /dev/null
+@@ -1,76 +0,0 @@
+-#ifndef __PROCESS_TIMER_HPP__
+-#define __PROCESS_TIMER_HPP__
+-
+-#include <stdlib.h> // For abort.
+-
+-#include <tr1/functional>
+-
+-#include <process/timeout.hpp>
+-
+-#include <stout/duration.hpp>
+-
+-namespace process {
+-
+-// Timer support!
+-
+-class Timer
+-{
+-public:
+- Timer() : id(0), pid(process::UPID()), thunk(&abort) {}
+-
+- static Timer create(
+- const Duration& duration,
+- const std::tr1::function<void(void)>& thunk);
+-
+- static bool cancel(const Timer& timer);
+-
+- bool operator == (const Timer& that) const
+- {
+- return id == that.id;
+- }
+-
+- // Invokes this timer's thunk.
+- void operator () () const
+- {
+- thunk();
+- }
+-
+- // Returns the timeout associated with this timer.
+- Timeout timeout() const
+- {
+- return t;
+- }
+-
+- // Returns the PID of the running process when this timer was
+- // created (via timers::create) or an empty PID if no process was
+- // running when this timer was created.
+- process::UPID creator() const
+- {
+- return pid;
+- }
+-
+-private:
+- Timer(long _id,
+- const Timeout& _t,
+- const process::UPID& _pid,
+- const std::tr1::function<void(void)>& _thunk)
+- : id(_id), t(_t), pid(_pid), thunk(_thunk)
+- {}
+-
+- uint64_t id; // Used for equality.
+-
+- Timeout t;
+-
+- // We store the PID of the "issuing" (i.e., "running") process (if
+- // there is one). We don't store a pointer to the process because we
+- // can't dereference it since it might no longer be valid. (Instead,
+- // the PID can be used internally to check if the process is still
+- // valid and get a refernce to it.)
+- process::UPID pid;
+-
+- std::tr1::function<void(void)> thunk;
+-};
+-
+-} // namespace process {
+-
+-#endif // __PROCESS_TIMER_HPP__
+diff --git a/3rdparty/libprocess/include/process/tuples/details.hpp b/3rdparty/libprocess/include/process/tuples/details.hpp
+deleted file mode 100644
+index 34a9fb5..0000000
+--- a/3rdparty/libprocess/include/process/tuples/details.hpp
++++ /dev/null
+@@ -1,170 +0,0 @@
+-template <MSGID ID> class tuple;
+-
+-#undef IDENTITY
+-#define IDENTITY(...) __VA_ARGS__
+-
+-#undef TUPLE
+-#define TUPLE(ID, types) \
+- template <> class tuple<ID> : public boost::tuple<IDENTITY types> \
+- { \
+- public: \
+- tuple(const boost::tuple<IDENTITY types> &t) \
+- : boost::tuple<IDENTITY types>(t) {} \
+- \
+- tuple(const std::string &data) \
+- { \
+- std::istringstream is(data); \
+- process::tuples::deserializer d(is); \
+- deserialize(d, *this); \
+- } \
+- \
+- operator std::string () const \
+- { \
+- std::ostringstream os; \
+- process::tuples::serializer s(os); \
+- serialize(s, *this); \
+- return os.str(); \
+- } \
+- }
+-
+-
+-inline void serialize(process::tuples::serializer &s,
+- const boost::tuples::null_type &)
+-{
+-}
+-
+-
+-template <typename H, typename T>
+-inline void serialize(process::tuples::serializer &s,
+- const boost::tuples::cons<H, T> &c)
+-{
+- s & c.get_head();
+- serialize(s, c.get_tail());
+-}
+-
+-
+-inline void deserialize(process::tuples::deserializer &d,
+- const boost::tuples::null_type &)
+-{
+-}
+-
+-
+-template <typename H, typename T>
+-inline void deserialize(process::tuples::deserializer &d,
+- boost::tuples::cons<H, T> &c)
+-{
+- d & c.get_head();
+- deserialize(d, c.get_tail());
+-}
+-
+-
+-template <MSGID ID>
+-tuple<ID> pack()
+-{
+- return tuple<ID>(::boost::make_tuple());
+-}
+-
+-
+-template <MSGID ID, typename T0>
+-tuple<ID> pack(const T0 &t0)
+-{
+- return tuple<ID>(::boost::make_tuple(t0));
+-}
+-
+-
+-template <MSGID ID, typename T0, typename T1>
+-tuple<ID> pack(const T0 &t0, const T1 &t1)
+-{
+- return tuple<ID>(::boost::make_tuple(t0, t1));
+-}
+-
+-
+-template <MSGID ID, typename T0, typename T1, typename T2>
+-tuple<ID> pack(const T0 &t0, const T1 &t1, const T2 &t2)
+-{
+- return tuple<ID>(::boost::make_tuple(t0, t1, t2));
+-}
+-
+-
+-template <MSGID ID,
+- typename T0, typename T1, typename T2, typename T3>
+-tuple<ID> pack(const T0 &t0, const T1 &t1, const T2 &t2, const T3 &t3)
+-{
+- return tuple<ID>(::boost::make_tuple(t0, t1, t2, t3));
+-}
+-
+-
+-template <MSGID ID,
+- typename T0, typename T1, typename T2, typename T3, typename T4>
+-tuple<ID> pack(const T0 &t0, const T1 &t1, const T2 &t2, const T3 &t3,
+- const T4 &t4)
+-{
+- return tuple<ID>(::boost::make_tuple(t0, t1, t2, t3, t4));
+-}
+-
+-
+-template <MSGID ID,
+- typename T0, typename T1, typename T2, typename T3, typename T4,
+- typename T5>
+-tuple<ID> pack(const T0 &t0, const T1 &t1, const T2 &t2, const T3 &t3,
+- const T4 &t4, const T5 &t5)
+-{
+- return tuple<ID>(::boost::make_tuple(t0, t1, t2, t3, t4, t5));
+-}
+-
+-
+-template <MSGID ID,
+- typename T0, typename T1, typename T2, typename T3, typename T4,
+- typename T5, typename T6>
+-tuple<ID> pack(const T0 &t0, const T1 &t1, const T2 &t2, const T3 &t3,
+- const T4 &t4, const T5 &t5, const T6 &t6)
+-{
+- return tuple<ID>(::boost::make_tuple(t0, t1, t2, t3, t4, t5, t6));
+-}
+-
+-
+-template <MSGID ID,
+- typename T0, typename T1, typename T2, typename T3, typename T4,
+- typename T5, typename T6, typename T7>
+-tuple<ID> pack(const T0 &t0, const T1 &t1, const T2 &t2, const T3 &t3,
+- const T4 &t4, const T5 &t5, const T6 &t6, const T7 &t7)
+-{
+- return tuple<ID>(::boost::make_tuple(t0, t1, t2, t3, t4, t5, t6, t7));
+-}
+-
+-
+-template <MSGID ID,
+- typename T0, typename T1, typename T2, typename T3, typename T4,
+- typename T5, typename T6, typename T7, typename T8>
+-tuple<ID> pack(const T0 &t0, const T1 &t1, const T2 &t2, const T3 &t3,
+- const T4 &t4, const T5 &t5, const T6 &t6, const T7 &t7,
+- const T8 &t8)
+-{
+- return tuple<ID>(::boost::make_tuple(t0, t1, t2, t3, t4, t5, t6, t7, t8));
+-}
+-
+-
+-template <MSGID ID,
+- typename T0, typename T1, typename T2, typename T3, typename T4,
+- typename T5, typename T6, typename T7, typename T8, typename T9>
+-tuple<ID> pack(const T0 &t0, const T1 &t1, const T2 &t2, const T3 &t3,
+- const T4 &t4, const T5 &t5, const T6 &t6, const T7 &t7,
+- const T8 &t8, const T9 &t9)
+-{
+- return tuple<ID>(::boost::make_tuple(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9));
+-}
+-
+-
+-template <MSGID ID>
+-tuple<ID> unpack(const std::string &data)
+-{
+- return tuple<ID>(data);
+-}
+-
+-
+-template <MSGID ID, int N>
+-typename boost::tuples::element<N, tuple<ID> >::type unpack(
+- const std::string &data)
+-{
+- return boost::tuples::get<N>(unpack<ID>(data));
+-}
+diff --git a/3rdparty/libprocess/include/process/tuples/tuples.hpp b/3rdparty/libprocess/include/process/tuples/tuples.hpp
+deleted file mode 100644
+index 16445fa..0000000
+--- a/3rdparty/libprocess/include/process/tuples/tuples.hpp
++++ /dev/null
+@@ -1,154 +0,0 @@
+-#ifndef __PROCESS_TUPLES_HPP__
+-#define __PROCESS_TUPLES_HPP__
+-
+-#include <stdlib.h>
+-
+-#include <arpa/inet.h>
+-
+-#include <sstream>
+-#include <string>
+-#include <utility>
+-
+-#include <boost/tuple/tuple.hpp>
+-
+-
+-namespace process { namespace tuples {
+-
+-// TODO(benh): Check stream errors! Report errors! Ahhhh!
+-
+-struct serializer
+-{
+- std::ostringstream& stream;
+-
+- serializer(std::ostringstream& s) : stream(s) {}
+-
+- void operator & (const int32_t & i)
+- {
+- uint32_t netInt = htonl((uint32_t) i);
+- stream.write((char *) &netInt, sizeof(netInt));
+- }
+-
+- void operator & (const int64_t & i)
+- {
+- uint32_t hiInt = htonl((uint32_t) (i >> 32));
+- uint32_t loInt = htonl((uint32_t) (i & 0xFFFFFFFF));
+- stream.write((char *) &hiInt, sizeof(hiInt));
+- stream.write((char *) &loInt, sizeof(loInt));
+- }
+-
+-#ifdef __APPLE__
+- void operator & (const intptr_t &i)
+- {
+- if (sizeof(intptr_t) == sizeof(int32_t))
+- *this & ((int32_t &) i);
+- else if (sizeof(intptr_t) == sizeof(int64_t))
+- *this & ((int64_t &) i);
+- else
+- abort();
+- }
+-#endif
+-
+- void operator & (const size_t &i)
+- {
+- if (sizeof(size_t) == sizeof(int32_t))
+- *this & ((int32_t &) i);
+- else if (sizeof(size_t) == sizeof(int64_t))
+- *this & ((int64_t &) i);
+- else
+- abort();
+- }
+-
+- void operator & (const double &d)
+- {
+- // TODO(*): Deal with endian issues?
+- stream.write((char *) &d, sizeof(d));
+- }
+-
+- void operator & (const std::string &s)
+- {
+- size_t size = s.size();
+- *this & (size);
+- stream.write(s.data(), size);
+- }
+-
+- void operator & (const PID &pid)
+- {
+- *this & ((int32_t) pid.pipe);
+- *this & ((int32_t) pid.ip);
+- *this & ((int32_t) pid.port);
+- }
+-};
+-
+-
+-struct deserializer
+-{
+- std::istringstream &stream;
+-
+- deserializer(std::istringstream &s) : stream(s) {}
+-
+- void operator & (int32_t &i)
+- {
+- uint32_t netInt;
+- stream.read((char *) &netInt, sizeof(netInt));
+- i = ntohl(netInt);
+- }
+-
+- void operator & (int64_t &i)
+- {
+- uint32_t hiInt, loInt;
+- stream.read((char *) &hiInt, sizeof(hiInt));
+- stream.read((char *) &loInt, sizeof(loInt));
+- int64_t hi64 = ntohl(hiInt);
+- int64_t lo64 = ntohl(loInt);
+- i = (hi64 << 32) | lo64;
+- }
+-
+-#ifdef __APPLE__
+- void operator & (intptr_t &i)
+- {
+- if (sizeof(intptr_t) == sizeof(int32_t))
+- *this & ((int32_t &) i);
+- else if (sizeof(intptr_t) == sizeof(int64_t))
+- *this & ((int64_t &) i);
+- else
+- abort();
+- }
+-#endif
+-
+- void operator & (size_t &i)
+- {
+- if (sizeof(size_t) == sizeof(int32_t))
+- *this & ((int32_t &) i);
+- else if (sizeof(size_t) == sizeof(int64_t))
+- *this & ((int64_t &) i);
+- else
+- abort();
+- }
+-
+- void operator & (double &d)
+- {
+- // TODO(*): Deal with endian issues?
+- stream.read((char *) &d, sizeof(d));
+- }
+-
+- void operator & (std::string &s)
+- {
+- size_t size;
+- *this & (size);
+- s.resize(size);
+- stream.read((char *) s.data(), size);
+- }
+-
+- void operator & (PID &pid)
+- {
+- *this & ((int32_t &) pid.pipe);
+- *this & ((int32_t &) pid.ip);
+- *this & ((int32_t &) pid.port);
+- }
+-};
+-
+-
+-}} // namespace process { namespace tuples {
+-
+-
+-#endif // __PROCESS_TUPLES_HPP__
+diff --git a/3rdparty/libprocess/src/config.hpp b/3rdparty/libprocess/src/config.hpp
+deleted file mode 100644
+index cbaf41d..0000000
+--- a/3rdparty/libprocess/src/config.hpp
++++ /dev/null
+@@ -1,38 +0,0 @@
+-#ifndef CONFIG_HPP
+-#define CONFIG_HPP
+-
+-#ifdef __sun__
+-#define gethostbyname2(name, _) gethostbyname(name)
+-#ifndef MSG_NOSIGNAL
+-#define MSG_NOSIGNAL 0
+-#endif
+-#ifndef SOL_TCP
+-#define SOL_TCP IPPROTO_TCP
+-#endif
+-#ifndef MAP_32BIT
+-#define MAP_32BIT 0
+-#endif
+-#endif /* __sun__ */
+-
+-#ifdef __APPLE__
+-#ifndef MAP_ANONYMOUS
+-#define MAP_ANONYMOUS MAP_ANON
+-#endif
+-#ifndef MSG_NOSIGNAL
+-#define MSG_NOSIGNAL 0
+-#endif
+-#ifndef SOL_TCP
+-#define SOL_TCP IPPROTO_TCP
+-#endif
+-#ifndef MAP_32BIT
+-#define MAP_32BIT 0
+-#endif
+-#endif /* __APPLE__ */
+-
+-#ifdef __linux__
+-#ifndef MAP_32BIT
+-#define MAP_32BIT 0
+-#endif
+-#endif /* __linux__ */
+-
+-#endif /* CONFIG_HPP */
+diff --git a/3rdparty/libprocess/src/decoder.hpp b/3rdparty/libprocess/src/decoder.hpp
+deleted file mode 100644
+index be410d9..0000000
+--- a/3rdparty/libprocess/src/decoder.hpp
++++ /dev/null
+@@ -1,437 +0,0 @@
+-#ifndef __DECODER_HPP__
+-#define __DECODER_HPP__
+-
+-#include <http_parser.h>
+-
+-#include <deque>
+-#include <string>
+-#include <vector>
+-
+-#include <process/http.hpp>
+-#include <process/socket.hpp>
+-
+-#include <stout/foreach.hpp>
+-#include <stout/gzip.hpp>
+-#include <stout/try.hpp>
+-
+-
+-// TODO(bmahler): Upgrade our http_parser to the latest version.
+-namespace process {
+-
+-// TODO: Make DataDecoder abstract and make RequestDecoder a concrete subclass.
+-class DataDecoder
+-{
+-public:
+- DataDecoder(const Socket& _s)
+- : s(_s), failure(false), request(NULL)
+- {
+- settings.on_message_begin = &DataDecoder::on_message_begin;
+- settings.on_header_field = &DataDecoder::on_header_field;
+- settings.on_header_value = &DataDecoder::on_header_value;
+- settings.on_url = &DataDecoder::on_url;
+- settings.on_body = &DataDecoder::on_body;
+- settings.on_headers_complete = &DataDecoder::on_headers_complete;
+- settings.on_message_complete = &DataDecoder::on_message_complete;
+-
+-#if !(HTTP_PARSER_VERSION_MAJOR >=2)
+- settings.on_path = &DataDecoder::on_path;
+- settings.on_fragment = &DataDecoder::on_fragment;
+- settings.on_query_string = &DataDecoder::on_query_string;
+-#endif
+-
+- http_parser_init(&parser, HTTP_REQUEST);
+-
+- parser.data = this;
+- }
+-
+- std::deque<http::Request*> decode(const char* data, size_t length)
+- {
+- size_t parsed = http_parser_execute(&parser, &settings, data, length);
+-
+- if (parsed != length) {
+- failure = true;
+- }
+-
+- if (!requests.empty()) {
+- std::deque<http::Request*> result = requests;
+- requests.clear();
+- return result;
+- }
+-
+- return std::deque<http::Request*>();
+- }
+-
+- bool failed() const
+- {
+- return failure;
+- }
+-
+- Socket socket() const
+- {
+- return s;
+- }
+-
+-private:
+- static int on_message_begin(http_parser* p)
+- {
+- DataDecoder* decoder = (DataDecoder*) p->data;
+-
+- assert(!decoder->failure);
+-
+- decoder->header = HEADER_FIELD;
+- decoder->field.clear();
+- decoder->value.clear();
+- decoder->query.clear();
+-
+- assert(decoder->request == NULL);
+- decoder->request = new http::Request();
+- decoder->request->headers.clear();
+- decoder->request->method.clear();
+- decoder->request->path.clear();
+- decoder->request->url.clear();
+- decoder->request->fragment.clear();
+- decoder->request->query.clear();
+- decoder->request->body.clear();
+-
+- return 0;
+- }
+-
+- static int on_headers_complete(http_parser* p)
+- {
+- DataDecoder* decoder = (DataDecoder*) p->data;
+-
+- // Add final header.
+- decoder->request->headers[decoder->field] = decoder->value;
+- decoder->field.clear();
+- decoder->value.clear();
+-
+- decoder->request->method = http_method_str((http_method) decoder->parser.method);
+- decoder->request->keepAlive = http_should_keep_alive(&decoder->parser);
+- return 0;
+- }
+-
+- static int on_message_complete(http_parser* p)
+- {
+- DataDecoder* decoder = (DataDecoder*) p->data;
+-// std::cout << "http::Request:" << std::endl;
+-// std::cout << " method: " << decoder->request->method << std::endl;
+-// std::cout << " path: " << decoder->request->path << std::endl;
+- // Parse the query key/values.
+- Try<std::string> decoded = http::decode(decoder->query);
+- if (decoded.isError()) {
+- return 1;
+- }
+- decoder->request->query = http::query::parse(decoded.get());
+-
+- Option<std::string> encoding =
+- decoder->request->headers.get("Content-Encoding");
+- if (encoding.isSome() && encoding.get() == "gzip") {
+- Try<std::string> decompressed = gzip::decompress(decoder->request->body);
+- if (decompressed.isError()) {
+- return 1;
+- }
+- decoder->request->body = decompressed.get();
+- decoder->request->headers["Content-Length"] =
+- decoder->request->body.length();
+- }
+-
+- decoder->requests.push_back(decoder->request);
+- decoder->request = NULL;
+- return 0;
+- }
+-
+- static int on_header_field(http_parser* p, const char* data, size_t length)
+- {
+- DataDecoder* decoder = (DataDecoder*) p->data;
+- assert(decoder->request != NULL);
+-
+- if (decoder->header != HEADER_FIELD) {
+- decoder->request->headers[decoder->field] = decoder->value;
+- decoder->field.clear();
+- decoder->value.clear();
+- }
+-
+- decoder->field.append(data, length);
+- decoder->header = HEADER_FIELD;
+-
+- return 0;
+- }
+-
+- static int on_header_value(http_parser* p, const char* data, size_t length)
+- {
+- DataDecoder* decoder = (DataDecoder*) p->data;
+- assert(decoder->request != NULL);
+- decoder->value.append(data, length);
+- decoder->header = HEADER_VALUE;
+- return 0;
+- }
+-
+- static int on_url(http_parser* p, const char* data, size_t length)
+- {
+- DataDecoder* decoder = (DataDecoder*) p->data;
+- assert(decoder->request != NULL);
+- decoder->request->url.append(data, length);
+- int ret = 0;
+-
+-#if (HTTP_PARSER_VERSION_MAJOR >=2)
+- // reworked parsing for version 2.0 &>
+- http_parser_url tUrlData;
+- ret = http_parser_parse_url(data, length, 0, &tUrlData);
+-
+- if (tUrlData.field_set & (1<<UF_PATH))
+- decoder->request->path.append(data+tUrlData.field_data[UF_PATH].off, tUrlData.field_data[UF_PATH].len);
+-
+- if (tUrlData.field_set & (1<<UF_FRAGMENT))
+- decoder->request->fragment.append(data+tUrlData.field_data[UF_FRAGMENT].off, tUrlData.field_data[UF_FRAGMENT].len);
+-
+- if (tUrlData.field_set & (1<<UF_QUERY))
+- decoder->query.append(data+tUrlData.field_data[UF_QUERY].off, tUrlData.field_data[UF_QUERY].len);
+-#endif
+-
+- return ret;
+- }
+-
+-#if !(HTTP_PARSER_VERSION_MAJOR >=2)
+- static int on_path(http_parser* p, const char* data, size_t length)
+- {
+- DataDecoder* decoder = (DataDecoder*) p->data;
+- assert(decoder->request != NULL);
+- decoder->request->path.append(data, length);
+- return 0;
+- }
+-
+- static int on_query_string(http_parser* p, const char* data, size_t length)
+- {
+- DataDecoder* decoder = (DataDecoder*) p->data;
+- assert(decoder->request != NULL);
+- decoder->query.append(data, length);
+- return 0;
+- }
+-
+- static int on_fragment(http_parser* p, const char* data, size_t length)
+- {
+- DataDecoder* decoder = (DataDecoder*) p->data;
+- assert(decoder->request != NULL);
+- decoder->request->fragment.append(data, length);
+- return 0;
+- }
+-#endif
+-
+- static int on_body(http_parser* p, const char* data, size_t length)
+- {
+- DataDecoder* decoder = (DataDecoder*) p->data;
+- assert(decoder->request != NULL);
+- decoder->request->body.append(data, length);
+- return 0;
+- }
+-
+- const Socket s; // The socket this decoder is associated with.
+-
+- bool failure;
+-
+- http_parser parser;
+- http_parser_settings settings;
+-
+- enum {
+- HEADER_FIELD,
+- HEADER_VALUE
+- } header;
+-
+- std::string field;
+- std::string value;
+- std::string query;
+-
+- http::Request* request;
+-
+- std::deque<http::Request*> requests;
+-};
+-
+-
+-class ResponseDecoder
+-{
+-public:
+- ResponseDecoder()
+- : failure(false), header(HEADER_FIELD), response(NULL)
+- {
+- settings.on_message_begin = &ResponseDecoder::on_message_begin;
+- settings.on_header_field = &ResponseDecoder::on_header_field;
+- settings.on_header_value = &ResponseDecoder::on_header_value;
+- settings.on_url = &ResponseDecoder::on_url;
+- settings.on_body = &ResponseDecoder::on_body;
+- settings.on_headers_complete = &ResponseDecoder::on_headers_complete;
+- settings.on_message_complete = &ResponseDecoder::on_message_complete;
+-
+-#if !(HTTP_PARSER_VERSION_MAJOR >=2)
+- settings.on_path = &ResponseDecoder::on_path;
+- settings.on_fragment = &ResponseDecoder::on_fragment;
+- settings.on_query_string = &ResponseDecoder::on_query_string;
+-#endif
+-
+- http_parser_init(&parser, HTTP_RESPONSE);
+-
+- parser.data = this;
+- }
+-
+- std::deque<http::Response*> decode(const char* data, size_t length)
+- {
+- size_t parsed = http_parser_execute(&parser, &settings, data, length);
+-
+- if (parsed != length) {
+- failure = true;
+- }
+-
+- if (!responses.empty()) {
+- std::deque<http::Response*> result = responses;
+- responses.clear();
+- return result;
+- }
+-
+- return std::deque<http::Response*>();
+- }
+-
+- bool failed() const
+- {
+- return failure;
+- }
+-
+-private:
+- static int on_message_begin(http_parser* p)
+- {
+- ResponseDecoder* decoder = (ResponseDecoder*) p->data;
+-
+- assert(!decoder->failure);
+-
+- decoder->header = HEADER_FIELD;
+- decoder->field.clear();
+- decoder->value.clear();
+-
+- assert(decoder->response == NULL);
+- decoder->response = new http::Response();
+- decoder->response->status.clear();
+- decoder->response->headers.clear();
+- decoder->response->type = http::Response::BODY;
+- decoder->response->body.clear();
+- decoder->response->path.clear();
+-
+- return 0;
+- }
+-
+- static int on_headers_complete(http_parser* p)
+- {
+- ResponseDecoder* decoder = (ResponseDecoder*) p->data;
+-
+- // Add final header.
+- decoder->response->headers[decoder->field] = decoder->value;
+- decoder->field.clear();
+- decoder->value.clear();
+-
+- return 0;
+- }
+-
+- static int on_message_complete(http_parser* p)
+- {
+- ResponseDecoder* decoder = (ResponseDecoder*) p->data;
+-
+- // Get the response status string.
+- if (http::statuses.contains(decoder->parser.status_code)) {
+- decoder->response->status = http::statuses[decoder->parser.status_code];
+- } else {
+- decoder->failure = true;
+- return 1;
+- }
+-
+- // We can only provide the gzip encoding.
+- Option<std::string> encoding =
+- decoder->response->headers.get("Content-Encoding");
+- if (encoding.isSome() && encoding.get() == "gzip") {
+- Try<std::string> decompressed = gzip::decompress(decoder->response->body);
+- if (decompressed.isError()) {
+- decoder->failure = true;
+- return 1;
+- }
+- decoder->response->body = decompressed.get();
+- decoder->response->headers["Content-Length"] =
+- decoder->response->body.length();
+- }
+-
+- decoder->responses.push_back(decoder->response);
+- decoder->response = NULL;
+- return 0;
+- }
+-
+- static int on_header_field(http_parser* p, const char* data, size_t length)
+- {
+- ResponseDecoder* decoder = (ResponseDecoder*) p->data;
+- assert(decoder->response != NULL);
+-
+- if (decoder->header != HEADER_FIELD) {
+- decoder->response->headers[decoder->field] = decoder->value;
+- decoder->field.clear();
+- decoder->value.clear();
+- }
+-
+- decoder->field.append(data, length);
+- decoder->header = HEADER_FIELD;
+-
+- return 0;
+- }
+-
+- static int on_header_value(http_parser* p, const char* data, size_t length)
+- {
+- ResponseDecoder* decoder = (ResponseDecoder*) p->data;
+- assert(decoder->response != NULL);
+- decoder->value.append(data, length);
+- decoder->header = HEADER_VALUE;
+- return 0;
+- }
+-
+- static int on_path(http_parser* p, const char* data, size_t length)
+- {
+- return 0;
+- }
+-
+- static int on_url(http_parser* p, const char* data, size_t length)
+- {
+- return 0;
+- }
+-
+- static int on_query_string(http_parser* p, const char* data, size_t length)
+- {
+- return 0;
+- }
+-
+- static int on_fragment(http_parser* p, const char* data, size_t length)
+- {
+- return 0;
+- }
+-
+- static int on_body(http_parser* p, const char* data, size_t length)
+- {
+- ResponseDecoder* decoder = (ResponseDecoder*) p->data;
+- assert(decoder->response != NULL);
+- decoder->response->body.append(data, length);
+- return 0;
+- }
+-
+- bool failure;
+-
+- http_parser parser;
+- http_parser_settings settings;
+-
+- enum {
+- HEADER_FIELD,
+- HEADER_VALUE
+- } header;
+-
+- std::string field;
+- std::string value;
+-
+- http::Response* response;
+-
+- std::deque<http::Response*> responses;
+-};
+-
+-
+-} // namespace process {
+-
+-#endif // __DECODER_HPP__
+diff --git a/3rdparty/libprocess/src/encoder.hpp b/3rdparty/libprocess/src/encoder.hpp
+deleted file mode 100644
+index 55aba22..0000000
+--- a/3rdparty/libprocess/src/encoder.hpp
++++ /dev/null
+@@ -1,262 +0,0 @@
+-#ifndef __ENCODER_HPP__
+-#define __ENCODER_HPP__
+-
+-#include <map>
+-#include <sstream>
+-
+-#include <process/http.hpp>
+-#include <process/process.hpp>
+-
+-#include <stout/foreach.hpp>
+-#include <stout/gzip.hpp>
+-#include <stout/hashmap.hpp>
+-#include <stout/numify.hpp>
+-#include <stout/os.hpp>
+-
+-// NOTE: We forward declare "ev_loop" and "ev_io" here because,
+-// on OSX, including "ev.h" causes conflict with "EV_ERROR" declared
+-// in "/usr/include/sys/event.h".
+-struct ev_loop;
+-struct ev_io;
+-
+-namespace process {
+-
+-const uint32_t GZIP_MINIMUM_BODY_LENGTH = 1024;
+-
+-typedef void (*Sender)(struct ev_loop*, ev_io*, int);
+-
+-extern void send_data(struct ev_loop*, ev_io*, int);
+-extern void send_file(struct ev_loop*, ev_io*, int);
+-
+-
+-class Encoder
+-{
+-public:
+- Encoder(const Socket& _s) : s(_s) {}
+- virtual ~Encoder() {}
+-
+- virtual Sender sender() = 0;
+-
+- Socket socket() const
+- {
+- return s;
+- }
+-
+-private:
+- const Socket s; // The socket this encoder is associated with.
+-};
+-
+-
+-class DataEncoder : public Encoder
+-{
+-public:
+- DataEncoder(const Socket& s, const std::string& _data)
+- : Encoder(s), data(_data), index(0) {}
+-
+- virtual ~DataEncoder() {}
+-
+- virtual Sender sender()
+- {
+- return send_data;
+- }
+-
+- virtual const char* next(size_t* length)
+- {
+- size_t temp = index;
+- index = data.size();
+- *length = data.size() - temp;
+- return data.data() + temp;
+- }
+-
+- virtual void backup(size_t length)
+- {
+- if (index >= length) {
+- index -= length;
+- }
+- }
+-
+- virtual size_t remaining() const
+- {
+- return data.size() - index;
+- }
+-
+-private:
+- const std::string data;
+- size_t index;
+-};
+-
+-
+-class MessageEncoder : public DataEncoder
+-{
+-public:
+- MessageEncoder(const Socket& s, Message* _message)
+- : DataEncoder(s, encode(_message)), message(_message) {}
+-
+- virtual ~MessageEncoder()
+- {
+- if (message != NULL) {
+- delete message;
+- }
+- }
+-
+- static std::string encode(Message* message)
+- {
+- if (message != NULL) {
+- std::ostringstream out;
+-
+- out << "POST /" << message->to.id << "/" << message->name
+- << " HTTP/1.0\r\n"
+- << "User-Agent: libprocess/" << message->from << "\r\n"
+- << "Connection: Keep-Alive\r\n";
+-
+- if (message->body.size() > 0) {
+- out << "Transfer-Encoding: chunked\r\n\r\n"
+- << std::hex << message->body.size() << "\r\n";
+- out.write(message->body.data(), message->body.size());
+- out << "\r\n"
+- << "0\r\n"
+- << "\r\n";
+- } else {
+- out << "\r\n";
+- }
+-
+- return out.str();
+- }
+- }
+-
+-private:
+- Message* message;
+-};
+-
+-
+-class HttpResponseEncoder : public DataEncoder
+-{
+-public:
+- HttpResponseEncoder(
+- const Socket& s,
+- const http::Response& response,
+- const http::Request& request)
+- : DataEncoder(s, encode(response, request)) {}
+-
+- static std::string encode(
+- const http::Response& response,
+- const http::Request& request)
+- {
+- std::ostringstream out;
+-
+- // TODO(benh): Check version?
+-
+- out << "HTTP/1.1 " << response.status << "\r\n";
+-
+- hashmap<std::string, std::string> headers = response.headers;
+-
+- // HTTP 1.1 requires the "Date" header. In the future once we
+- // start checking the version (above) then we can conditionally
+- // add this header, but for now, we always do.
+- time_t rawtime;
+- time(&rawtime);
+-
+- char date[256];
+-
+- // TODO(benh): Check return code of strftime!
+- strftime(date, 256, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&rawtime));
+-
+- headers["Date"] = date;
+-
+- // Should we compress this response?
+- std::string body = response.body;
+-
+- if (response.type == http::Response::BODY &&
+- response.body.length() >= GZIP_MINIMUM_BODY_LENGTH &&
+- !headers.contains("Content-Encoding") &&
+- request.accepts("gzip")) {
+- Try<std::string> compressed = gzip::compress(body);
+- if (compressed.isError()) {
+- LOG(WARNING) << "Failed to gzip response body: " << compressed.error();
+- } else {
+- body = compressed.get();
+- headers["Content-Length"] = stringify(body.length());
+- headers["Content-Encoding"] = "gzip";
+- }
+- }
+-
+- foreachpair (const std::string& key, const std::string& value, headers) {
+- out << key << ": " << value << "\r\n";
+- }
+-
+- // Add a Content-Length header if the response is of type "none"
+- // or "body" and no Content-Length header has been supplied.
+- if (response.type == http::Response::NONE &&
+- !headers.contains("Content-Length")) {
+- out << "Content-Length: 0\r\n";
+- } else if (response.type == http::Response::BODY &&
+- !headers.contains("Content-Length")) {
+- out << "Content-Length: " << body.size() << "\r\n";
+- }
+-
+- // Use a CRLF to mark end of headers.
+- out << "\r\n";
+-
+- // Add the body if necessary.
+- if (response.type == http::Response::BODY) {
+- // If the Content-Length header was supplied, only write as much data
+- // as the length specifies.
+- Result<uint32_t> length = numify<uint32_t>(headers.get("Content-Length"));
+- if (length.isSome() && length.get() <= body.length()) {
+- out.write(body.data(), length.get());
+- } else {
+- out.write(body.data(), body.size());
+- }
+- }
+-
+- return out.str();
+- }
+-};
+-
+-
+-class FileEncoder : public Encoder
+-{
+-public:
+- FileEncoder(const Socket& s, int _fd, size_t _size)
+- : Encoder(s), fd(_fd), size(_size), index(0) {}
+-
+- virtual ~FileEncoder()
+- {
+- os::close(fd);
+- }
+-
+- virtual Sender sender()
+- {
+- return send_file;
+- }
+-
+- virtual int next(off_t* offset, size_t* length)
+- {
+- off_t temp = index;
+- index = size;
+- *offset = temp;
+- *length = size - temp;
+- return fd;
+- }
+-
+- virtual void backup(size_t length)
+- {
+- if (index >= length) {
+- index -= length;
+- }
+- }
+-
+- virtual size_t remaining() const
+- {
+- return size - index;
+- }
+-
+-private:
+- int fd;
+- size_t size;
+- off_t index;
+-};
+-
+-} // namespace process {
+-
+-#endif // __ENCODER_HPP__
+diff --git a/3rdparty/libprocess/src/fatal.cpp b/3rdparty/libprocess/src/fatal.cpp
+deleted file mode 100644
+index b2934e0..0000000
+--- a/3rdparty/libprocess/src/fatal.cpp
++++ /dev/null
+@@ -1,26 +0,0 @@
+-#include <stdarg.h>
+-#include <stdio.h>
+-#include <stdlib.h>
+-
+-void __fatal(const char *file, int line, const char *fmt, ...)
+-{
+- va_list args;
+- va_start(args, fmt);
+- vfprintf(stderr, fmt, args);
+- fprintf(stderr, " (%s:%u)\n", file, line);
+- fflush(stderr);
+- va_end(args);
+- exit(1);
+-}
+-
+-void __fatalerror(const char *file, int line, const char *fmt, ...)
+-{
+- va_list args;
+- va_start(args, fmt);
+- vfprintf(stderr, fmt, args);
+- fprintf(stderr, " (%s:%u): ", file, line);
+- perror(NULL);
+- fflush(stderr);
+- va_end(args);
+- exit(1);
+-}
+diff --git a/3rdparty/libprocess/src/fatal.hpp b/3rdparty/libprocess/src/fatal.hpp
+deleted file mode 100644
+index 38646f3..0000000
+--- a/3rdparty/libprocess/src/fatal.hpp
++++ /dev/null
+@@ -1,28 +0,0 @@
+-/*
+- * Basic perror + exit routines.
+- *
+- * Contributed by Benjamin Hindman <benh at berkeley.edu>, 2008.
+- */
+-
+-#ifndef FATAL_HPP
+-#define FATAL_HPP
+-
+-#include <stdarg.h>
+-#include <stdio.h>
+-#include <stdlib.h>
+-
+-/*
+- * Like the non-debug version except includes the file name and line
+- * number in the output.
+- */
+-#define fatal(fmt...) __fatal(__FILE__, __LINE__, fmt)
+-void __fatal(const char *file, int line, const char *fmt, ...);
+-
+-/*
+- * Like the non-debug version except includes the file name and line
+- * number in the output.
+- */
+-#define fatalerror(fmt...) __fatalerror(__FILE__, __LINE__, fmt)
+-void __fatalerror(const char *file, int line, const char *fmt, ...);
+-
+-#endif /* FATAL_HPP */
+diff --git a/3rdparty/libprocess/src/gate.hpp b/3rdparty/libprocess/src/gate.hpp
+deleted file mode 100644
+index 954f620..0000000
+--- a/3rdparty/libprocess/src/gate.hpp
++++ /dev/null
+@@ -1,103 +0,0 @@
+-#ifndef GATE_H
+-#define GATE_H
+-
+-/* TODO(benh): Provide an implementation directly on-top-of futex's for Linux. */
+-//#ifdef __linux__
+-//#else
+-
+-class Gate
+-{
+-public:
+- typedef intptr_t state_t;
+-
+-private:
+- int waiters;
+- state_t state;
+- pthread_mutex_t mutex;
+- pthread_cond_t cond;
+-
+-public:
+- Gate() : waiters(0), state(0)
+- {
+- pthread_mutex_init(&mutex, NULL);
+- pthread_cond_init(&cond, NULL);
+- }
+-
+- ~Gate()
+- {
+- pthread_cond_destroy(&cond);
+- pthread_mutex_destroy(&mutex);
+- }
+-
+- void open(bool all = true)
+- {
+- pthread_mutex_lock(&mutex);
+- {
+- state++;
+- if (all) pthread_cond_broadcast(&cond);
+- else pthread_cond_signal(&cond);
+- }
+- pthread_mutex_unlock(&mutex);
+- }
+-
+- void wait()
+- {
+- pthread_mutex_lock(&mutex);
+- {
+- waiters++;
+- state_t old = state;
+- while (old == state)
+- pthread_cond_wait(&cond, &mutex);
+- waiters--;
+- }
+- pthread_mutex_unlock(&mutex);
+- }
+-
+- state_t approach()
+- {
+- state_t old;
+- pthread_mutex_lock(&mutex);
+- {
+- waiters++;
+- old = state;
+- }
+- pthread_mutex_unlock(&mutex);
+- return old;
+- }
+-
+- void arrive(state_t old)
+- {
+- pthread_mutex_lock(&mutex);
+- {
+- while (old == state) {
+- pthread_cond_wait(&cond, &mutex);
+- }
+- waiters--;
+- }
+- pthread_mutex_unlock(&mutex);
+- }
+-
+- void leave()
+- {
+- pthread_mutex_lock(&mutex);
+- {
+- waiters--;
+- }
+- pthread_mutex_unlock(&mutex);
+- }
+-
+- bool empty()
+- {
+- bool occupied = true;
+- pthread_mutex_lock(&mutex);
+- {
+- occupied = waiters > 0 ? true : false;
+- }
+- pthread_mutex_unlock(&mutex);
+- return !occupied;
+- }
+-};
+-
+-//#endif
+-
+-#endif /* GATE_H */
+diff --git a/3rdparty/libprocess/src/httpd.cpp b/3rdparty/libprocess/src/httpd.cpp
+deleted file mode 100644
+index bbd5251..0000000
+--- a/3rdparty/libprocess/src/httpd.cpp
++++ /dev/null
+@@ -1,306 +0,0 @@
+-/* TODO(benh): TCP_CORK!!!!! */
+-/* TODO(benh): Complete HttpParser & HttpMessage implementation. */
+-/* TODO(benh): Turn off Nagle (on TCP_NODELAY) for pipelined requests. */
+-
+-#include <string.h>
+-
+-#include <process.hpp>
+-#include <tuple.hpp>
+-
+-#include <iostream>
+-#include <map>
+-#include <sstream>
+-#include <stdexcept>
+-
+-#include <arpa/inet.h>
+-
+-#include <stout/os.hpp>
+-
+-#include "net.hpp"
+-
+-#include "http-parser/http_parser.h"
+-
+-using std::cerr;
+-using std::cout;
+-using std::endl;
+-using std::runtime_error;
+-using std::string;
+-using std::map;
+-
+-using process::tuple::Tuple;
+-
+-#define malloc(bytes) \
+- ({ void *tmp; if ((tmp = malloc(bytes)) == NULL) abort(); tmp; })
+-
+-#define realloc(address, bytes) \
+- ({ void *tmp; if ((tmp = realloc(address, bytes)) == NULL) abort(); tmp; })
+-
+-#define HTTP_500 \
+- "HTTP/1.1 500 Internal Server Error\r\n\r\n"
+-
+-#define HTTP_501 \
+- "HTTP/1.1 501 Not Implemented\r\n\r\n"
+-
+-#define HTTP_404 \
+- "HTTP/1.1 404 Not Found\r\n\r\n"
+-
+-
+-struct HttpMessage
+-{
+- unsigned short method;
+- /* TODO(*): Use HTTP_MAX_URI_SIZE. */
+- string uri;
+-};
+-
+-
+-class HttpParser
+-{
+-protected:
+- static int on_uri(http_parser *parser, const char *p, size_t len)
+- {
+- HttpMessage *message = (HttpMessage *) parser->data;
+- message->uri += string(p, len);
+- return 0;
+- }
+-
+- static int on_headers_complete(http_parser *parser)
+- {
+- HttpMessage *message = (HttpMessage *) parser->data;
+- message->method = parser->method;
+- return 0;
+- }
+-
+-public:
+- static HttpMessage * parse(const string &raw)
+- {
+- http_parser parser;
+- http_parser_init(&parser, HTTP_REQUEST);
+-
+- HttpMessage *message = new HttpMessage;
+-
+- parser.data = message;
+-
+- parser.on_message_begin = NULL;
+- parser.on_header_field = NULL;
+- parser.on_header_value = NULL;
+- parser.on_path = NULL;
+- parser.on_uri = &HttpParser::on_uri;
+- parser.on_fragment = NULL;
+- parser.on_query_string = NULL;
+- parser.on_body = NULL;
+- parser.on_headers_complete = &HttpParser::on_headers_complete;
+- parser.on_message_complete = NULL;
+-
+- http_parser_execute(&parser, raw.c_str(), raw.length());
+-
+- if (http_parser_has_error(&parser)) {
+- //cerr << "parser error" << endl;
+- abort();
+- }
+-
+- return message;
+- }
+-};
+-
+-
+-class HttpConnection : public SocketProcess<TCP>
+-{
+-protected:
+- void operator () ()
+- {
+- //cout << ht_id() << ": running " << this << " connection (1)" << endl;
+-
+- string raw;
+-
+- /* Read headers (until CRLF CRLF). */
+- do {
+- char buf[512];
+- ssize_t len = recv(buf, 512);
+- raw += string(buf, len);
+- } while (raw.find("\r\n\r\n") == string::npos);
+-
+- //cout << ht_id() << ": running " << this << " connection (2)" << endl;
+-
+- /* Parse headers. */
+- HttpMessage *message = HttpParser::parse(raw);
+-
+- /* Handle request. */
+- switch (message->method) {
+- case HTTP_GET: {
+- message->uri =
+- message->uri != "/"
+- ? "." + message->uri
+- : "./index.html";
+-
+- //cout << "URI: " << message->uri << endl;
+-
+- /* Open file (if possible). */
+- int fd;
+-
+- if ((fd = open(message->uri.c_str(), O_RDONLY, 0)) < 0) {
+- send(HTTP_404, strlen(HTTP_404));
+- return;
+- }
+-
+- /* Lookup file size. */
+- struct stat fd_stat;
+-
+- if (fstat(fd, &fd_stat) < 0) {
+- send(HTTP_500, strlen(HTTP_500));
+- os::close(fd);
+- return;
+- }
+-
+- /* TODO(benh): Use TCP_CORK. */
+-
+- /* Transmit reply header. */
+- std::stringstream out;
+-
+- out << "HTTP/1.1 200 OK\r\n";
+-
+- /* Determine the content type. */
+- if (message->uri.find(".jpg") != string::npos) {
+- out << "Content-Type: image/jpeg\r\n";
+- } else if (message->uri.find(".gif") != string::npos) {
+- out << "Content-Type: image/gif\r\n";
+- } else if (message->uri.find(".png") != string::npos) {
+- out << "Content-Type: image/png\r\n";
+- } else if (message->uri.find(".css") != string::npos) {
+- out << "Content-Type: text/css\r\n";
+- } else {
+- out << "Content-Type: text/html\r\n";
+- }
+-
+- out <<
+- "Content-Length: " << fd_stat.st_size << "\r\n"
+- "\r\n";
+-
+- //cout << out.str() << endl;
+-
+- send(out.str().c_str(), out.str().size());
+-
+- //cout << ht_id() << ": running " << this << " connection (3)" << endl;
+-
+- /* Transmit file (TODO(benh): Use file cache.). */
+- sendfile(fd, fd_stat.st_size);
+-
+- //cout << ht_id() << ": running " << this << " connection (4)" << endl;
+-
+- os::close(fd);
+-
+- break;
+- }
+-
+- default:
+- /* Unimplemented. */
+- send(HTTP_501, strlen(HTTP_501));
+- break;
+- }
+- }
+-
+-public:
+- HttpConnection(int s) : SocketProcess<TCP>(s) {}
+- ~HttpConnection() {}
+-};
+-
+-
+-enum HTTP_MESSAGES { ACCEPT = PROCESS_MSGID };
+-
+-namespace process { namespace tuple { TUPLE(::ACCEPT, (int)); }}
+-
+-class HttpAcceptor : public Tuple< Acceptor<TCP> >
+-{
+-private:
+- PID server;
+-
+-protected:
+- void operator () ()
+- {
+- do {
+- struct sockaddr_in addr;
+- int c = accept(addr);
+- //cout << ht_id() << ": running acceptor" << endl;
+- send<ACCEPT>(server, c);
+- } while (true);
+- }
+-
+-public:
+- HttpAcceptor(const PID &_server, int s) : server(_server) { socket(s); }
+-};
+-
+-
+-
+-class HttpServer : public Tuple< Server<TCP> >
+-{
+-private:
+- map<PID, HttpConnection *> connections;
+-
+-protected:
+- void operator () ()
+- {
+- int on = 1;
+- setsockopt(SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+- bind();
+- listen(100000);
+-
+- HttpAcceptor *acceptor = new HttpAcceptor(self(), s);
+- link(spawn(acceptor));
+-
+- do {
+- switch (receive()) {
+- case ACCEPT: {
+- //cout << ht_id() << ": running server (accept)" << endl;
+- int c;
+- unpack<ACCEPT>(c);
+- HttpConnection *connection = new HttpConnection(c);
+- connections[link(spawn(connection))] = connection;
+- //cout << "...started (" << connection << ")..." << endl;
+- break;
+- }
+- case PROCESS_EXIT: {
+- //cout << ht_id() << ": running server (exit)" << endl;
+- if (from() == acceptor->getPID()) {
+- throw runtime_error("unimplemented acceptor failure");
+- } else if (connections.find(from()) != connections.end()) {
+- HttpConnection *connection = connections[from()];
+- connections.erase(from());
+- delete connection;
+- //cout << "...finished (" << connection << ")..." << endl;
+- }
+- break;
+- }
+- default:
+- cout << "HttpServer received unexpected message" << endl;
+- break;
+- }
+- } while (true);
+- }
+-
+-public:
+- HttpServer(int port) { init(INADDR_ANY, port); }
+-};
+-
+-
+-
+-int main(int argc, char **argv)
+-{
+- /* TODO(benh): Blah, 'sendfile' doesn't let us use MSG_NOSIGNAL. :( */
+- signal(SIGPIPE, SIG_IGN);
+-
+- if (argc != 2) {
+- cerr << "usage: " << argv[0] << " <port>" << endl;
+- return -1;
+- }
+-
+-#ifdef USE_LITHE
+- ProcessScheduler *scheduler = new ProcessScheduler();
+- Process::spawn(new HttpServer(atoi(argv[1])));
+- /* TODO(benh): Make Process::wait take and use the hart if using Lithe! */
+- for (;;)
+- sleep(10000);
+-#else
+- Process::wait(Process::spawn(new HttpServer(atoi(argv[1]))));
+-#endif /* USE_LITHE */
+-
+- return 0;
+-}
+diff --git a/3rdparty/libprocess/src/latch.cpp b/3rdparty/libprocess/src/latch.cpp
+deleted file mode 100644
+index a6f1256..0000000
+--- a/3rdparty/libprocess/src/latch.cpp
++++ /dev/null
+@@ -1,62 +0,0 @@
+-#include <process/id.hpp>
+-#include <process/latch.hpp>
+-#include <process/process.hpp>
+-
+-#include <stout/duration.hpp>
+-
+-namespace process {
+-
+-// TODO(benh): Provide an "optimized" implementation of a latch that
+-// is libprocess aware. That is, allow integrate "waiting" on a latch
+-// within libprocess such that it doesn't cost a memory allocation, a
+-// spawn, a message send, a wait, and two user-space context-switchs.
+-
+-Latch::Latch()
+-{
+- triggered = false;
+-
+- // Deadlock is possible if one thread is trying to delete a latch
+- // but the libprocess thread(s) is trying to acquire a resource the
+- // deleting thread is holding. Hence, we only save the PID for
+- // triggering the latch and let the GC actually do the deleting
+- // (thus no waiting is necessary, and deadlocks are avoided).
+- pid = spawn(new ProcessBase(ID::generate("__latch__")), true);
+-}
+-
+-
+-Latch::~Latch()
+-{
+- terminate(pid);
+-}
+-
+-
+-void Latch::trigger()
+-{
+- if (!triggered) {
+- terminate(pid);
+- triggered = true;
+- }
+-}
+-
+-
+-bool Latch::await(const Duration& duration)
+-{
+- if (!triggered) {
+- process::wait(pid, duration); // Explict to disambiguate.
+- // It's possible that we failed to wait because:
+- // (1) Our process has already terminated.
+- // (2) We timed out (i.e., duration was not "infinite").
+-
+- // In the event of (1) we might need to return 'true' since a
+- // terminated process might imply that the latch has been
+- // triggered. To capture this we simply return the value of
+- // 'triggered' (which will also capture cases where we actually
+- // timed out but have since triggered, which seems like an
+- // acceptable semantics given such a "tie").
+- return triggered;
+- }
+-
+- return true;
+-}
+-
+-} // namespace process {
+diff --git a/3rdparty/libprocess/src/net.hpp b/3rdparty/libprocess/src/net.hpp
+deleted file mode 100644
+index 2fdc62a..0000000
+--- a/3rdparty/libprocess/src/net.hpp
++++ /dev/null
+@@ -1,231 +0,0 @@
+-/* TODO(benh): Write a form of 'Client' process. */
+-
+-#ifndef NET_HPP
+-#define NET_HPP
+-
+-#include <assert.h>
+-#include <errno.h>
+-#include <fcntl.h>
+-
+-#include <process.hpp>
+-
+-#include <netinet/in.h>
+-#include <netinet/tcp.h>
+-#include <netinet/udp.h>
+-
+-#include <sys/ioctl.h>
+-#include <sys/sendfile.h>
+-#include <sys/socket.h>
+-
+-#include <stdexcept>
+-#include <iostream>
+-
+-#include <stout/os.hpp>
+-
+-typedef enum Protocol { TCP = SOCK_STREAM, UDP = SOCK_DGRAM } Protocol;
+-
+-using std::runtime_error;
+-using std::string;
+-
+-
+-template <Protocol protocol>
+-class SocketProcess : public Process
+-{
+-protected:
+- int s;
+-
+- void setsockopt(int level, int optname, const void *optval, socklen_t optlen)
+- {
+- if (::setsockopt(s, level, optname, optval, optlen) < 0)
+- throw std::runtime_error(string("setsockopt: ") += strerror(errno));
+- }
+-
+- virtual void socket()
+- {
+- if ((s = ::socket(AF_INET, protocol, IPPROTO_IP)) < 0)
+- throw runtime_error(string("socket: ") += strerror(errno));
+-
+- socket(s);
+- }
+-
+- virtual void socket(int sd)
+- {
+- s = sd;
+-
+- int flags = 1;
+- if (ioctl(s, FIONBIO, &flags) &&
+- ((flags = fcntl(s, F_GETFL, 0)) < 0 ||
+- fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0))
+- throw runtime_error(string("ioctl/fcntl: ") += strerror(errno));
+-
+- if (fcntl(s, F_SETFD, FD_CLOEXEC) < 0) {
+- throw runtime_error(string("fcntl: ") += strerror(errno));
+- }
+- }
+-
+- virtual void bind(in_addr_t ip, in_port_t port)
+- {
+- struct sockaddr_in addr;
+- addr.sin_family = PF_INET;
+- addr.sin_addr.s_addr = ip;
+- addr.sin_port = htons(port);
+-
+- if (::bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+- throw runtime_error(string("bind: ") += strerror(errno));
+- }
+-
+- virtual ssize_t recv(void *buf, size_t bytes)
+- {
+- ssize_t len = 0;
+- do {
+- len = ::recv(s, buf, bytes, 0);
+-
+- if (len > 0)
+- return len;
+- else if (len < 0 && errno == EWOULDBLOCK)
+- while (!await(s, RDONLY));
+- else if (len == 0)
+- throw runtime_error(string("recv: connection terminated"));
+- else
+- throw runtime_error(string("recv: ") += strerror(errno));
+- } while (!(len > 0));
+-
+- return len;
+- }
+-
+- virtual ssize_t recvall(void *buf, size_t bytes)
+- {
+- ssize_t len, offset = 0;
+- do {
+- len = ::recv(s, (char *) buf + offset, bytes - offset, 0);
+-
+- if (len > 0)
+- offset += len;
+- else if (len < 0 && errno == EWOULDBLOCK)
+- while (!await(s, RDONLY));
+- else if (len == 0)
+- throw runtime_error(string("recvall: connection terminated"));
+- else
+- throw runtime_error(string("recvall: ") += strerror(errno));
+- } while (offset != bytes);
+-
+- return offset;
+- }
+-
+- virtual void send(const void *buf, size_t bytes)
+- {
+- size_t offset = 0;
+- do {
+- size_t len =
+- ::send(s, (char *) buf + offset, bytes - offset, MSG_NOSIGNAL);
+-
+- if (len > 0)
+- offset += len;
+- else if (len < 0 && errno == EWOULDBLOCK)
+- while (!await(s, WRONLY));
+- else if (len == 0)
+- throw runtime_error(string("send: connection terminated"));
+- else
+- throw runtime_error(string("send: ") += strerror(errno));
+- } while (offset != bytes);
+- }
+-
+- virtual void sendfile(int fd, size_t bytes)
+- {
+- off_t offset = 0;
+- do {
+- size_t len = ::sendfile(s, fd, 0, bytes - offset);
+-
+- if (len > 0)
+- offset += len;
+- else if (len < 0 && errno == EWOULDBLOCK)
+- while (!await(s, WRONLY));
+- else if (len == 0)
+- throw runtime_error(string("sendfile: connection terminated"));
+- else
+- throw runtime_error(string("sendfile: ") += strerror(errno));
+- } while (offset != bytes);
+- }
+-
+-public:
+- SocketProcess() : s(-1) {}
+- SocketProcess(int _s) : s(_s)
+- {
+- int flags = 1;
+- if (ioctl(s, FIONBIO, &flags) &&
+- ((flags = fcntl(s, F_GETFL, 0)) < 0 ||
+- fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0))
+- throw runtime_error(string("ioctl/fcntl: ") += strerror(errno));
+- }
+- ~SocketProcess() { os::close(s); }
+-};
+-
+-
+-template <Protocol protocol>
+-class Acceptor : public SocketProcess<protocol>
+-{
+-protected:
+- virtual int accept(struct sockaddr_in &addr)
+- {
+- int c;
+-
+- do {
+- while (!await(SocketProcess<protocol>::s, Process::RDONLY));
+-
+- size_t size = sizeof(struct sockaddr_in);
+-
+- c = ::accept(SocketProcess<protocol>::s,
+- (struct sockaddr *) &addr,
+- (socklen_t *) &size);
+-
+- if (c == 0)
+- throw runtime_error(string("accept: ") += strerror(errno));
+- else if (c < 0 && (errno != EWOULDBLOCK))
+- throw runtime_error(string("accept: ") += strerror(errno));
+- } while (!(c > 0));
+-
+- return c;
+- }
+-
+-public:
+- Acceptor() {}
+- Acceptor(int s) : SocketProcess<protocol>(s) {}
+-};
+-
+-
+-template <Protocol protocol>
+-class Server : public Acceptor<protocol>
+-{
+-protected:
+- in_addr_t ip;
+- in_port_t port;
+-
+- void init(in_addr_t _ip = INADDR_ANY, in_port_t _port = 0)
+- {
+- ip = _ip;
+- port = _port;
+- SocketProcess<protocol>::socket();
+- }
+-
+- virtual void listen(int n)
+- {
+- int &s = SocketProcess<protocol>::s;
+- if (::listen(s, n) < 0)
+- throw runtime_error(string("listen: ") += strerror(errno));
+- }
+-
+- virtual void bind()
+- {
+- SocketProcess<protocol>::bind(ip, port);
+- }
+-
+-public:
+- Server(in_addr_t _ip = INADDR_ANY, in_port_t _port = 0)
+- : ip(_ip), port(_port)
+- {
+- SocketProcess<protocol>::socket();
+- }
+-};
+-
+-
+-#endif /* NET_HH */
+diff --git a/3rdparty/libprocess/src/pid.cpp b/3rdparty/libprocess/src/pid.cpp
+deleted file mode 100644
+index becc46b..0000000
+--- a/3rdparty/libprocess/src/pid.cpp
++++ /dev/null
+@@ -1,179 +0,0 @@
+-#include <errno.h>
+-#include <netdb.h>
+-#include <stdio.h>
+-#include <string.h>
+-
+-#include <arpa/inet.h>
+-
+-#include <glog/logging.h>
+-
+-#include <iostream>
+-#include <string>
+-
+-#include <boost/unordered_map.hpp>
+-
+-#include <process/pid.hpp>
+-#include <process/process.hpp>
+-
+-#include <stout/os.hpp>
+-
+-#include "config.hpp"
+-
+-
+-using std::istream;
+-using std::ostream;
+-using std::size_t;
+-using std::string;
+-
+-
+-namespace process {
+-
+-UPID::UPID(const char* s)
+-{
+- std::istringstream in(s);
+- in >> *this;
+-}
+-
+-
+-UPID::UPID(const std::string& s)
+-{
+- std::istringstream in(s);
+- in >> *this;
+-}
+-
+-
+-// TODO(benh): Make this inline-able (cyclic dependency issues).
+-UPID::UPID(const ProcessBase& process)
+-{
+- id = process.self().id;
+- ip = process.self().ip;
+- port = process.self().port;
+-}
+-
+-
+-UPID::operator std::string() const
+-{
+- std::ostringstream out;
+- out << *this;
+- return out.str();
+-}
+-
+-
+-ostream& operator << (ostream& stream, const UPID& pid)
+-{
+- // Call inet_ntop since inet_ntoa is not thread-safe!
+- char ip[INET_ADDRSTRLEN];
+- if (inet_ntop(AF_INET, (in_addr *) &pid.ip, ip, INET_ADDRSTRLEN) == NULL)
+- memset(ip, 0, INET_ADDRSTRLEN);
+-
+- stream << pid.id << "@" << ip << ":" << pid.port;
+- return stream;
+-}
+-
+-
+-istream& operator >> (istream& stream, UPID& pid)
+-{
+- pid.id = "";
+- pid.ip = 0;
+- pid.port = 0;
+-
+- string str;
+- if (!(stream >> str)) {
+- stream.setstate(std::ios_base::badbit);
+- return stream;
+- }
+-
+- VLOG(2) << "Attempting to parse '" << str << "' into a PID";
+-
+- if (str.size() == 0) {
+- stream.setstate(std::ios_base::badbit);
+- return stream;
+- }
+-
+- string id;
+- string host;
+- uint32_t ip;
+- uint16_t port;
+-
+- size_t index = str.find('@');
+-
+- if (index != string::npos) {
+- id = str.substr(0, index);
+- } else {
+- stream.setstate(std::ios_base::badbit);
+- return stream;
+- }
+-
+- str = str.substr(index + 1);
+-
+- index = str.find(':');
+-
+- if (index != string::npos) {
+- host = str.substr(0, index);
+- } else {
+- stream.setstate(std::ios_base::badbit);
+- return stream;
+- }
+-
+- hostent he, *hep;
+- char* temp;
+- size_t length;
+- int result;
+- int herrno;
+-
+- // Allocate temporary buffer for gethostbyname2_r.
+- length = 1024;
+- temp = new char[length];
+-
+- while ((result = gethostbyname2_r(
+- host.c_str(), AF_INET, &he, temp, length, &hep, &herrno)) == ERANGE) {
+- // Enlarge the buffer.
+- delete[] temp;
+- length *= 2;
+- temp = new char[length];
+- }
+-
+- if (result != 0 || hep == NULL) {
+- VLOG(2) << "Failed to parse host '" << host
+- << "' because " << hstrerror(herrno);
+- delete[] temp;
+- stream.setstate(std::ios_base::badbit);
+- return stream;
+- }
+-
+- if (hep->h_addr_list[0] == NULL) {
+- VLOG(2) << "Got no addresses for '" << host << "'";
+- delete[] temp;
+- stream.setstate(std::ios_base::badbit);
+- return stream;
+- }
+-
+- ip = *((uint32_t*) hep->h_addr_list[0]);
+-
+- delete[] temp;
+-
+- str = str.substr(index + 1);
+-
+- if (sscanf(str.c_str(), "%hu", &port) != 1) {
+- stream.setstate(std::ios_base::badbit);
+- return stream;
+- }
+-
+- pid.id = id;
+- pid.ip = ip;
+- pid.port = port;
+-
+- return stream;
+-}
+-
+-
+-size_t hash_value(const UPID& pid)
+-{
+- size_t seed = 0;
+- boost::hash_combine(seed, pid.id);
+- boost::hash_combine(seed, pid.ip);
+- boost::hash_combine(seed, pid.port);
+- return seed;
+-}
+-
+-} // namespace process {
+diff --git a/3rdparty/libprocess/src/process.cpp b/3rdparty/libprocess/src/process.cpp
+deleted file mode 100644
+index 2d193b1..0000000
+--- a/3rdparty/libprocess/src/process.cpp
++++ /dev/null
+@@ -1,3708 +0,0 @@
+-#include <errno.h>
+-#include <ev.h>
+-#include <limits.h>
+-#include <libgen.h>
+-#include <netdb.h>
+-#include <pthread.h>
+-#include <signal.h>
+-#include <stdarg.h>
+-#include <stdio.h>
+-#include <stdlib.h>
+-#include <string.h>
+-#include <unistd.h>
+-
+-#include <arpa/inet.h>
+-
+-#include <glog/logging.h>
+-
+-#include <netinet/in.h>
+-#include <netinet/tcp.h>
+-
+-#include <sys/ioctl.h>
+-#include <sys/mman.h>
+-#include <sys/select.h>
+-#include <sys/socket.h>
+-#include <sys/time.h>
+-#include <sys/types.h>
+-#include <sys/uio.h>
+-
+-#include <algorithm>
+-#include <deque>
+-#include <fstream>
+-#include <iomanip>
+-#include <iostream>
+-#include <list>
+-#include <map>
+-#include <queue>
+-#include <set>
+-#include <sstream>
+-#include <stack>
+-#include <stdexcept>
+-#include <vector>
+-
+-#include <tr1/functional>
+-#include <tr1/memory> // TODO(benh): Replace all shared_ptr with unique_ptr.
+-
+-#include <boost/shared_array.hpp>
+-
+-#include <process/clock.hpp>
+-#include <process/defer.hpp>
+-#include <process/delay.hpp>
+-#include <process/dispatch.hpp>
+-#include <process/executor.hpp>
+-#include <process/filter.hpp>
+-#include <process/future.hpp>
+-#include <process/gc.hpp>
+-#include <process/help.hpp>
+-#include <process/id.hpp>
+-#include <process/io.hpp>
+-#include <process/logging.hpp>
+-#include <process/mime.hpp>
+-#include <process/process.hpp>
+-#include <process/profiler.hpp>
+-#include <process/socket.hpp>
+-#include <process/statistics.hpp>
+-#include <process/time.hpp>
+-#include <process/timer.hpp>
+-
+-#include <stout/duration.hpp>
+-#include <stout/foreach.hpp>
+-#include <stout/lambda.hpp>
+-#include <stout/net.hpp>
+-#include <stout/os.hpp>
+-#include <stout/strings.hpp>
+-#include <stout/thread.hpp>
+-
+-#include "config.hpp"
+-#include "decoder.hpp"
+-#include "encoder.hpp"
+-#include "gate.hpp"
+-#include "synchronized.hpp"
+-
+-using process::wait; // Necessary on some OS's to disambiguate.
+-
+-using process::http::BadRequest;
+-using process::http::InternalServerError;
+-using process::http::NotFound;
+-using process::http::OK;
+-using process::http::Request;
+-using process::http::Response;
+-using process::http::ServiceUnavailable;
+-
+-using std::deque;
+-using std::find;
+-using std::list;
+-using std::map;
+-using std::ostream;
+-using std::pair;
+-using std::queue;
+-using std::set;
+-using std::stack;
+-using std::string;
+-using std::stringstream;
+-using std::vector;
+-
+-// Represents a remote "node" (encapsulates IP address and port).
+-class Node
+-{
+-public:
+- Node(uint32_t _ip = 0, uint16_t _port = 0)
+- : ip(_ip), port(_port) {}
+-
+- bool operator < (const Node& that) const
+- {
+- if (ip == that.ip) {
+- return port < that.port;
+- } else {
+- return ip < that.ip;
+- }
+- }
+-
+- ostream& operator << (ostream& stream) const
+- {
+- stream << ip << ":" << port;
+- return stream;
+- }
+-
+- uint32_t ip;
+- uint16_t port;
+-};
+-
+-
+-namespace process {
+-
+-namespace ID {
+-
+-string generate(const string& prefix)
+-{
+- static map<string, int> prefixes;
+- static synchronizable(prefixes) = SYNCHRONIZED_INITIALIZER;
+-
+- int id;
+- synchronized (prefixes) {
+- int& _id = prefixes[prefix];
+- _id += 1;
+- id = _id;
+- }
+- return prefix + "(" + stringify(id) + ")";
+-}
+-
+-} // namespace ID {
+-
+-
+-namespace http {
+-
+-hashmap<uint16_t, string> statuses;
+-
+-} // namespace http {
+-
+-
+-namespace mime {
+-
+-map<string, string> types;
+-
+-} // namespace mime {
+-
+-
+-// Provides reference counting semantics for a process pointer.
+-class ProcessReference
+-{
+-public:
+- ProcessReference() : process(NULL) {}
+-
+- ~ProcessReference()
+- {
+- cleanup();
+- }
+-
+- ProcessReference(const ProcessReference& that)
+- {
+- copy(that);
+- }
+-
+- ProcessReference& operator = (const ProcessReference& that)
+- {
+- if (this != &that) {
+- cleanup();
+- copy(that);
+- }
+- return *this;
+- }
+-
+- ProcessBase* operator -> ()
+- {
+- return process;
+- }
+-
+- operator ProcessBase* ()
+- {
+- return process;
+- }
+-
+- operator bool () const
+- {
+- return process != NULL;
+- }
+-
+-private:
+- friend class ProcessManager; // For ProcessManager::use.
+-
+- ProcessReference(ProcessBase* _process)
+- : process(_process)
+- {
+- if (process != NULL) {
+- __sync_fetch_and_add(&(process->refs), 1);
+- }
+- }
+-
+- void copy(const ProcessReference& that)
+- {
+- process = that.process;
+-
+- if (process != NULL) {
+- // There should be at least one reference to the process, so
+- // we don't need to worry about checking if it's exiting or
+- // not, since we know we can always create another reference.
+- CHECK(process->refs > 0);
+- __sync_fetch_and_add(&(process->refs), 1);
+- }
+- }
+-
+- void cleanup()
+- {
+- if (process != NULL) {
+- __sync_fetch_and_sub(&(process->refs), 1);
+- }
+- }
+-
+- ProcessBase* process;
+-};
+-
+-
+-// Provides a process that manages sending HTTP responses so as to
+-// satisfy HTTP/1.1 pipelining. Each request should either enqueue a
+-// response, or ask the proxy to handle a future response. The process
+-// is responsible for making sure the responses are sent in the same
+-// order as the requests. Note that we use a 'Socket' in order to keep
+-// the underyling file descriptor from getting closed while there
+-// might still be outstanding responses even though the client might
+-// have closed the connection (see more discussion in
+-// SocketManger::close and SocketManager::proxy).
+-class HttpProxy : public Process<HttpProxy>
+-{
+-public:
+- HttpProxy(const Socket& _socket);
+- virtual ~HttpProxy();
+-
+- // Enqueues the response to be sent once all previously enqueued
+- // responses have been processed (e.g., waited for and sent).
+- void enqueue(const Response& response, const Request& request);
+-
+- // Enqueues a future to a response that will get waited on (up to
+- // some timeout) and then sent once all previously enqueued
+- // responses have been processed (e.g., waited for and sent).
+- void handle(Future<Response>* future, const Request& request);
+-
+-private:
+- // Starts "waiting" on the next available future response.
+- void next();
+-
+- // Invoked once a future response has been satisfied.
+- void waited(const Future<Response>& future);
+-
+- // Demuxes and handles a response.
+- bool process(const Future<Response>& future, const Request& request);
+-
+- // Handles stream (i.e., pipe) based responses.
+- void stream(const Future<short>& poll, const Request& request);
+-
+- Socket socket; // Wrap the socket to keep it from getting closed.
+-
+- // Describes a queue "item" that wraps the future to the response
+- // and the original request.
+- // The original request contains needed information such as what encodings
+- // are acceptable and whether to persist the connection.
+- struct Item
+- {
+- Item(const Request& _request, Future<Response>* _future)
+- : request(_request), future(_future) {}
+-
+- ~Item()
+- {
+- delete future;
+- }
+-
+- const Request request; // Make a copy.
+- Future<Response>* future;
+- };
+-
+- queue<Item*> items;
+-
+- Option<int> pipe; // Current pipe, if streaming.
+-};
+-
+-
+-// Helper for creating routes without a process.
+-// TODO(benh): Move this into route.hpp.
+-class Route
+-{
+-public:
+- Route(const string& name,
+- const Option<string>& help,
+- const lambda::function<Future<Response>(const Request&)>& handler)
+- {
+- process = new RouteProcess(name, help, handler);
+- spawn(process);
+- }
+-
+- ~Route()
+- {
+- terminate(process);
+- wait(process);
+- }
+-
+-private:
+- class RouteProcess : public Process<RouteProcess>
+- {
+- public:
+- RouteProcess(
+- const string& name,
+- const Option<string>& _help,
+- const lambda::function<Future<Response>(const Request&)>& _handler)
+- : ProcessBase(strings::remove(name, "/", strings::PREFIX)),
+- help(_help),
+- handler(_handler) {}
+-
+- protected:
+- virtual void initialize()
+- {
+- route("/", help, &RouteProcess::handle);
+- }
+-
+- Future<Response> handle(const Request& request)
+- {
+- return handler(request);
+- }
+-
+- const Option<string> help;
+- const lambda::function<Future<Response>(const Request&)> handler;
+- };
+-
+- RouteProcess* process;
+-};
+-
+-
+-class SocketManager
+-{
+-public:
+- SocketManager();
+- ~SocketManager();
+-
+- Socket accepted(int s);
+-
+- void link(ProcessBase* process, const UPID& to);
+-
+- PID<HttpProxy> proxy(const Socket& socket);
+-
+- void send(Encoder* encoder, bool persist);
+- void send(const Response& response,
+- const Request& request,
+- const Socket& socket);
+- void send(Message* message);
+-
+- Encoder* next(int s);
+-
+- void close(int s);
+-
+- void exited(const Node& node);
+- void exited(ProcessBase* process);
+-
+-private:
+- // Map from UPID (local/remote) to process.
+- map<UPID, set<ProcessBase*> > links;
+-
+- // Collection of all actice sockets.
+- map<int, Socket> sockets;
+-
+- // Collection of sockets that should be disposed when they are
+- // finished being used (e.g., when there is no more data to send on
+- // them).
+- set<int> dispose;
+-
+- // Map from socket to node (ip, port).
+- map<int, Node> nodes;
+-
+- // Maps from node (ip, port) to temporary sockets (i.e., they will
+- // get closed once there is no more data to send on them).
+- map<Node, int> temps;
+-
+- // Maps from node (ip, port) to persistent sockets (i.e., they will
+- // remain open even if there is no more data to send on them). We
+- // distinguish these from the 'temps' collection so we can tell when
+- // a persistant socket has been lost (and thus generate
+- // ExitedEvents).
+- map<Node, int> persists;
+-
+- // Map from socket to outgoing queue.
+- map<int, queue<Encoder*> > outgoing;
+-
+- // HTTP proxies.
+- map<int, HttpProxy*> proxies;
+-
+- // Protects instance variables.
+- synchronizable(this);
+-};
+-
+-
+-class ProcessManager
+-{
+-public:
+- ProcessManager(const string& delegate);
+- ~ProcessManager();
+-
+- ProcessReference use(const UPID& pid);
+-
+- bool handle(
+- const Socket& socket,
+- Request* request);
+-
+- bool deliver(
+- ProcessBase* receiver,
+- Event* event,
+- ProcessBase* sender = NULL);
+-
+- bool deliver(
+- const UPID& to,
+- Event* event,
+- ProcessBase* sender = NULL);
+-
+- UPID spawn(ProcessBase* process, bool manage);
+- void resume(ProcessBase* process);
+- void cleanup(ProcessBase* process);
+- void link(ProcessBase* process, const UPID& to);
+- void terminate(const UPID& pid, bool inject, ProcessBase* sender = NULL);
+- bool wait(const UPID& pid);
+-
+- void enqueue(ProcessBase* process);
+- ProcessBase* dequeue();
+-
+- void settle();
+-
+- // The /__processes__ route.
+- Future<Response> __processes__(const Request&);
+-
+-private:
+- // Delegate process name to receive root HTTP requests.
+- const string delegate;
+-
+- // Map of all local spawned and running processes.
+- map<string, ProcessBase*> processes;
+- synchronizable(processes);
+-
+- // Gates for waiting threads (protected by synchronizable(processes)).
+- map<ProcessBase*, Gate*> gates;
+-
+- // Queue of runnable processes (implemented using list).
+- list<ProcessBase*> runq;
+- synchronizable(runq);
+-
+- // Number of running processes, to support Clock::settle operation.
+- int running;
+-};
+-
+-
+-// Help strings.
+-const string Logging::TOGGLE_HELP = HELP(
+- TLDR(
+- "Sets the logging verbosity level for a specified duration."),
+- USAGE(
+- "/logging/toggle?level=VALUE&duration=VALUE"),
+- DESCRIPTION(
+- "The libprocess library uses [glog][glog] for logging. The library",
+- "only uses verbose logging which means nothing will be output unless",
+- "the verbosity level is set (by default it's 0, libprocess uses"
+- "levels 1, 2, and 3).",
+- "",
+- "**NOTE:** If your application uses glog this will also affect",
+- "your verbose logging.",
+- "",
+- "Required query parameters:",
+- "",
+- "> level=VALUE Verbosity level (e.g., 1, 2, 3)",
+- "> duration=VALUE Duration to keep verbosity level",
+- "> toggled (e.g., 10secs, 15mins, etc.)"),
+- REFERENCES(
+- "[glog]: https://code.google.com/p/google-glog"));
+-
+-
+-const string Profiler::START_HELP = HELP(
+- TLDR(
+- "Starts profiling ..."),
+- USAGE(
+- "/profiler/start..."),
+- DESCRIPTION(
+- "...",
+- "",
+- "Query parameters:",
+- "",
+- "> param=VALUE Some description here"));
+-
+-
+-const string Profiler::STOP_HELP = HELP(
+- TLDR(
+- "Stops profiling ..."),
+- USAGE(
+- "/profiler/stop..."),
+- DESCRIPTION(
+- "...",
+- "",
+- "Query parameters:",
+- "",
+- "> param=VALUE Some description here"));
+-
+-
+-// Unique id that can be assigned to each process.
+-static uint32_t __id__ = 0;
+-
+-// Local server socket.
+-static int __s__ = -1;
+-
+-// Local IP address.
+-static uint32_t __ip__ = 0;
+-
+-// Local port.
+-static uint16_t __port__ = 0;
+-
+-// Active SocketManager (eventually will probably be thread-local).
+-static SocketManager* socket_manager = NULL;
+-
+-// Active ProcessManager (eventually will probably be thread-local).
+-static ProcessManager* process_manager = NULL;
+-
+-// Event loop.
+-static struct ev_loop* loop = NULL;
+-
+-// Asynchronous watcher for interrupting loop.
+-static ev_async async_watcher;
+-
+-// Watcher for timeouts.
+-static ev_timer timeouts_watcher;
+-
+-// Server watcher for accepting connections.
+-static ev_io server_watcher;
+-
+-// Queue of I/O watchers.
+-static queue<ev_io*>* watchers = new queue<ev_io*>();
+-static synchronizable(watchers) = SYNCHRONIZED_INITIALIZER;
+-
+-// We store the timers in a map of lists indexed by the timeout of the
+-// timer so that we can have two timers that have the same timeout. We
+-// exploit that the map is SORTED!
+-static map<Time, list<Timer> >* timeouts =
+- new map<Time, list<Timer> >();
+-static synchronizable(timeouts) = SYNCHRONIZED_INITIALIZER_RECURSIVE;
+-
+-// For supporting Clock::settle(), true if timers have been removed
+-// from 'timeouts' but may not have been executed yet. Protected by
+-// the timeouts lock. This is only used when the clock is paused.
+-static bool pending_timers = false;
+-
+-// Flag to indicate whether or to update the timer on async interrupt.
+-static bool update_timer = false;
+-
+-// Scheduling gate that threads wait at when there is nothing to run.
+-static Gate* gate = new Gate();
+-
+-// Filter. Synchronized support for using the filterer needs to be
+-// recursive incase a filterer wants to do anything fancy (which is
+-// possible and likely given that filters will get used for testing).
+-static Filter* filterer = NULL;
+-static synchronizable(filterer) = SYNCHRONIZED_INITIALIZER_RECURSIVE;
+-
+-// Global garbage collector.
+-PID<GarbageCollector> gc;
+-
+-// Global help.
+-PID<Help> help;
+-
+-// Per thread process pointer.
+-ThreadLocal<ProcessBase>* _process_ = new ThreadLocal<ProcessBase>();
+-
+-// Per thread executor pointer.
+-ThreadLocal<Executor>* _executor_ = new ThreadLocal<Executor>();
+-
+-const Duration LIBPROCESS_STATISTICS_WINDOW = Days(1);
+-
+-// We namespace the clock related variables to keep them well
+-// named. In the future we'll probably want to associate a clock with
+-// a specific ProcessManager/SocketManager instance pair, so this will
+-// likely change.
+-namespace clock {
+-
+-map<ProcessBase*, Time>* currents = new map<ProcessBase*, Time>();
+-
+-Time initial = Time::EPOCH;
+-Time current = Time::EPOCH;
+-
+-bool paused = false;
+-
+-} // namespace clock {
+-
+-
+-Time Time::EPOCH = Time(Duration::zero());
+-
+-
+-Time Time::MAX = Time(Duration::max());
+-
+-
+-Time Clock::now()
+-{
+- return now(__process__);
+-}
+-
+-
+-Time Clock::now(ProcessBase* process)
+-{
+- synchronized (timeouts) {
+- if (Clock::paused()) {
+- if (process != NULL) {
+- if (clock::currents->count(process) != 0) {
+- return (*clock::currents)[process];
+- } else {
+- return (*clock::currents)[process] = clock::initial;
+- }
+- } else {
+- return clock::current;
+- }
+- }
+- }
+-
+- // TODO(benh): Versus ev_now()?
+- double d = ev_time();
+- Try<Time> time = Time::create(d);
+-
+- // TODO(xujyan): Move CHECK_SOME to libprocess and add CHECK_SOME
+- // here.
+- if (time.isError()) {
+- LOG(FATAL) << "Failed to create a Time from " << d << ": "
+- << time.error();
+- }
+- return time.get();
+-}
+-
+-
+-void Clock::pause()
+-{
+- process::initialize(); // To make sure the libev watchers are ready.
+-
+- synchronized (timeouts) {
+- if (!clock::paused) {
+- clock::initial = clock::current = now();
+- clock::paused = true;
+- VLOG(2) << "Clock paused at " << clock::initial;
+- }
+- }
+-
+- // Note that after pausing the clock an existing libev timer might
+- // still fire (invoking handle_timeout), but since paused == true no
+- // "time" will actually have passed, so no timer will actually fire.
+-}
+-
+-
+-bool Clock::paused()
+-{
+- return clock::paused;
+-}
+-
+-
+-void Clock::resume()
+-{
+- process::initialize(); // To make sure the libev watchers are ready.
+-
+- synchronized (timeouts) {
+- if (clock::paused) {
+- VLOG(2) << "Clock resumed at " << clock::current;
+- clock::paused = false;
+- clock::currents->clear();
+- update_timer = true;
+- ev_async_send(loop, &async_watcher);
+- }
+- }
+-}
+-
+-
+-void Clock::advance(const Duration& duration)
+-{
+- synchronized (timeouts) {
+- if (clock::paused) {
+- clock::current += duration;
+- VLOG(2) << "Clock advanced (" << duration << ") to " << clock::current;
+- if (!update_timer) {
+- update_timer = true;
+- ev_async_send(loop, &async_watcher);
+- }
+- }
+- }
+-}
+-
+-
+-void Clock::advance(ProcessBase* process, const Duration& duration)
+-{
+- synchronized (timeouts) {
+- if (clock::paused) {
+- Time current = now(process);
+- current += duration;
+- (*clock::currents)[process] = current;
+- VLOG(2) << "Clock of " << process->self() << " advanced (" << duration
+- << ") to " << current;
+- }
+- }
+-}
+-
+-
+-void Clock::update(const Time& time)
+-{
+- synchronized (timeouts) {
+- if (clock::paused) {
+- if (clock::current < time) {
+- clock::current = Time(time);
+- VLOG(2) << "Clock updated to " << clock::current;
+- if (!update_timer) {
+- update_timer = true;
+- ev_async_send(loop, &async_watcher);
+- }
+- }
+- }
+- }
+-}
+-
+-
+-void Clock::update(ProcessBase* process, const Time& time)
+-{
+- synchronized (timeouts) {
+- if (clock::paused) {
+- if (now(process) < time) {
+- VLOG(2) << "Clock of " << process->self() << " updated to " << time;
+- (*clock::currents)[process] = Time(time);
+- }
+- }
+- }
+-}
+-
+-
+-void Clock::order(ProcessBase* from, ProcessBase* to)
+-{
+- update(to, now(from));
+-}
+-
+-
+-void Clock::settle()
+-{
+- CHECK(clock::paused); // TODO(benh): Consider returning a bool instead.
+- process_manager->settle();
+-}
+-
+-
+-static Message* encode(const UPID& from,
+- const UPID& to,
+- const string& name,
+- const string& data = "")
+-{
+- Message* message = new Message();
+- message->from = from;
+- message->to = to;
+- message->name = name;
+- message->body = data;
+- return message;
+-}
+-
+-
+-static void transport(Message* message, ProcessBase* sender = NULL)
+-{
+- if (message->to.ip == __ip__ && message->to.port == __port__) {
+- // Local message.
+- process_manager->deliver(message->to, new MessageEvent(message), sender);
+- } else {
+- // Remote message.
+- socket_manager->send(message);
+- }
+-}
+-
+-
+-static bool libprocess(Request* request)
+-{
+- return request->method == "POST" &&
+- request->headers.count("User-Agent") > 0 &&
+- request->headers["User-Agent"].find("libprocess/") == 0;
+-}
+-
+-
+-static Message* parse(Request* request)
+-{
+- // TODO(benh): Do better error handling (to deal with a malformed
+- // libprocess message, malicious or otherwise).
+- const string& agent = request->headers["User-Agent"];
+- const string& identifier = "libprocess/";
+- size_t index = agent.find(identifier);
+- if (index != string::npos) {
+- // Okay, now determine 'from'.
+- const UPID from(agent.substr(index + identifier.size(), agent.size()));
+-
+- // Now determine 'to'.
+- index = request->path.find('/', 1);
+- index = index != string::npos ? index - 1 : string::npos;
+- const UPID to(request->path.substr(1, index), __ip__, __port__);
+-
+- // And now determine 'name'.
+- index = index != string::npos ? index + 2: request->path.size();
+- const string& name = request->path.substr(index);
+-
+- VLOG(2) << "Parsed message name '" << name
+- << "' for " << to << " from " << from;
+-
+- Message* message = new Message();
+- message->name = name;
+- message->from = from;
+- message->to = to;
+- message->body = request->body;
+-
+- return message;
+- }
+-
+- return NULL;
+-}
+-
+-
+-void handle_async(struct ev_loop* loop, ev_async* _, int revents)
+-{
+- synchronized (watchers) {
+- // Start all the new I/O watchers.
+- while (!watchers->empty()) {
+- ev_io* watcher = watchers->front();
+- watchers->pop();
+- ev_io_start(loop, watcher);
+- }
+- }
+-
+- synchronized (timeouts) {
+- if (update_timer) {
+- if (!timeouts->empty()) {
+- // Determine when the next timer should fire.
+- timeouts_watcher.repeat = (timeouts->begin()->first - Clock::now()).secs();
+-
+- if (timeouts_watcher.repeat <= 0) {
+- // Feed the event now!
+- timeouts_watcher.repeat = 0;
+- ev_timer_again(loop, &timeouts_watcher);
+- ev_feed_event(loop, &timeouts_watcher, EV_TIMEOUT);
+- } else {
+- // Don't fire the timer if the clock is paused since we
+- // don't want time to advance (instead a call to
+- // clock::advance() will handle the timer).
+- if (Clock::paused() && timeouts_watcher.repeat > 0) {
+- timeouts_watcher.repeat = 0;
+- }
+-
+- ev_timer_again(loop, &timeouts_watcher);
+- }
+- }
+-
+- update_timer = false;
+- }
+- }
+-}
+-
+-
+-void handle_timeouts(struct ev_loop* loop, ev_timer* _, int revents)
+-{
+- list<Timer> timedout;
+-
+- synchronized (timeouts) {
+- Time now = Clock::now();
+-
+- VLOG(3) << "Handling timeouts up to " << now;
+-
+- foreachkey (const Time& timeout, *timeouts) {
+- if (timeout > now) {
+- break;
+- }
+-
+- VLOG(3) << "Have timeout(s) at " << timeout;
+-
+- // Record that we have pending timers to execute so the
+- // Clock::settle() operation can wait until we're done.
+- pending_timers = true;
+-
+- foreach (const Timer& timer, (*timeouts)[timeout]) {
+- timedout.push_back(timer);
+- }
+- }
+-
+- // Now erase the range of timeouts that timed out.
+- timeouts->erase(timeouts->begin(), timeouts->upper_bound(now));
+-
+- // Okay, so the timeout for the next timer should not have fired.
+- CHECK(timeouts->empty() || (timeouts->begin()->first > now));
+-
+- // Update the timer as necessary.
+- if (!timeouts->empty()) {
+- // Determine when the next timer should fire.
+- timeouts_watcher.repeat =
+- (timeouts->begin()->first - Clock::now()).secs();
+-
+- if (timeouts_watcher.repeat <= 0) {
+- // Feed the event now!
+- timeouts_watcher.repeat = 0;
+- ev_timer_again(loop, &timeouts_watcher);
+- ev_feed_event(loop, &timeouts_watcher, EV_TIMEOUT);
+- } else {
+- // Don't fire the timer if the clock is paused since we don't
+- // want time to advance (instead a call to Clock::advance()
+- // will handle the timer).
+- if (Clock::paused() && timeouts_watcher.repeat > 0) {
+- timeouts_watcher.repeat = 0;
+- }
+-
+- ev_timer_again(loop, &timeouts_watcher);
+- }
+- }
+-
+- update_timer = false; // Since we might have a queued update_timer.
+- }
+-
+- // Update current time of process (if it's present/valid). It might
+- // be necessary to actually add some more synchronization around
+- // this so that, for example, pausing and resuming the clock doesn't
+- // cause some processes to get thier current times updated and
+- // others not. Since ProcessManager::use acquires the 'processes'
+- // lock we had to move this out of the synchronized (timeouts) above
+- // since there was a deadlock with acquring 'processes' then
+- // 'timeouts' (reverse order) in ProcessManager::cleanup. Note that
+- // current time may be greater than the timeout if a local message
+- // was received (and happens-before kicks in).
+- if (Clock::paused()) {
+- foreach (const Timer& timer, timedout) {
+- if (ProcessReference process = process_manager->use(timer.creator())) {
+- Clock::update(process, timer.timeout().time());
+- }
+- }
+- }
+-
+- // Invoke the timers that timed out (TODO(benh): Do this
+- // asynchronously so that we don't tie up the event thread!).
+- foreach (const Timer& timer, timedout) {
+- timer();
+- }
+-
+- // Mark ourselves as done executing the timers since it's now safe
+- // for a call to Clock::settle() to check if there will be any
+- // future timeouts reached.
+- synchronized (timeouts) {
+- pending_timers = false;
+- }
+-}
+-
+-
+-void recv_data(struct ev_loop* loop, ev_io* watcher, int revents)
+-{
+- DataDecoder* decoder = (DataDecoder*) watcher->data;
+-
+- int s = watcher->fd;
+-
+- while (true) {
+- const ssize_t size = 80 * 1024;
+- ssize_t length = 0;
+-
+- char data[size];
+-
+- length = recv(s, data, size, 0);
+-
+- if (length < 0 && (errno == EINTR)) {
+- // Interrupted, try again now.
+- continue;
+- } else if (length < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+- // Might block, try again later.
+- break;
+- } else if (length <= 0) {
+- // Socket error or closed.
+- if (length < 0) {
+- const char* error = strerror(errno);
+- VLOG(1) << "Socket error while receiving: " << error;
+- } else {
+- VLOG(1) << "Socket closed while receiving";
+- }
+- socket_manager->close(s);
+- delete decoder;
+- ev_io_stop(loop, watcher);
+- delete watcher;
+- break;
+- } else {
+- CHECK(length > 0);
+-
+- // Decode as much of the data as possible into HTTP requests.
+- const deque<Request*>& requests = decoder->decode(data, length);
+-
+- if (!requests.empty()) {
+- foreach (Request* request, requests) {
+- process_manager->handle(decoder->socket(), request);
+- }
+- } else if (requests.empty() && decoder->failed()) {
+- VLOG(1) << "Decoder error while receiving";
+- socket_manager->close(s);
+- delete decoder;
+- ev_io_stop(loop, watcher);
+- delete watcher;
+- break;
+- }
+- }
+- }
+-}
+-
+-
+-void send_data(struct ev_loop* loop, ev_io* watcher, int revents)
+-{
+- DataEncoder* encoder = (DataEncoder*) watcher->data;
+-
+- int s = watcher->fd;
+-
+- while (true) {
+- const void* data;
+- size_t size;
+-
+- data = encoder->next(&size);
+- CHECK(size > 0);
+-
+- ssize_t length = send(s, data, size, MSG_NOSIGNAL);
+-
+- if (length < 0 && (errno == EINTR)) {
+- // Interrupted, try again now.
+- encoder->backup(size);
+- continue;
+- } else if (length < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+- // Might block, try again later.
+- encoder->backup(size);
+- break;
+- } else if (length <= 0) {
+- // Socket error or closed.
+- if (length < 0) {
+- const char* error = strerror(errno);
+- VLOG(1) << "Socket error while sending: " << error;
+- } else {
+- VLOG(1) << "Socket closed while sending";
+- }
+- socket_manager->close(s);
+- delete encoder;
+- ev_io_stop(loop, watcher);
+- delete watcher;
+- break;
+- } else {
+- CHECK(length > 0);
+-
+- // Update the encoder with the amount sent.
+- encoder->backup(size - length);
+-
+- // See if there is any more of the message to send.
+- if (encoder->remaining() == 0) {
+- delete encoder;
+-
+- // Stop this watcher for now.
+- ev_io_stop(loop, watcher);
+-
+- // Check for more stuff to send on socket.
+- Encoder* next = socket_manager->next(s);
+- if (next != NULL) {
+- watcher->data = next;
+- ev_io_init(watcher, next->sender(), s, EV_WRITE);
+- ev_io_start(loop, watcher);
+- } else {
+- // Nothing more to send right now, clean up.
+- delete watcher;
+- }
+- break;
+- }
+- }
+- }
+-}
+-
+-
+-void send_file(struct ev_loop* loop, ev_io* watcher, int revents)
+-{
+- FileEncoder* encoder = (FileEncoder*) watcher->data;
+-
+- int s = watcher->fd;
+-
+- while (true) {
+- int fd;
+- off_t offset;
+- size_t size;
+-
+- fd = encoder->next(&offset, &size);
+- CHECK(size > 0);
+-
+- ssize_t length = os::sendfile(s, fd, offset, size);
+-
+- if (length < 0 && (errno == EINTR)) {
+- // Interrupted, try again now.
+- encoder->backup(size);
+- continue;
+- } else if (length < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+- // Might block, try again later.
+- encoder->backup(size);
+- break;
+- } else if (length <= 0) {
+- // Socket error or closed.
+- if (length < 0) {
+- const char* error = strerror(errno);
+- VLOG(1) << "Socket error while sending: " << error;
+- } else {
+- VLOG(1) << "Socket closed while sending";
+- }
+- socket_manager->close(s);
+- delete encoder;
+- ev_io_stop(loop, watcher);
+- delete watcher;
+- break;
+- } else {
+- CHECK(length > 0);
+-
+- // Update the encoder with the amount sent.
+- encoder->backup(size - length);
+-
+- // See if there is any more of the message to send.
+- if (encoder->remaining() == 0) {
+- delete encoder;
+-
+- // Stop this watcher for now.
+- ev_io_stop(loop, watcher);
+-
+- // Check for more stuff to send on socket.
+- Encoder* next = socket_manager->next(s);
+- if (next != NULL) {
+- watcher->data = next;
+- ev_io_init(watcher, next->sender(), s, EV_WRITE);
+- ev_io_start(loop, watcher);
+- } else {
+- // Nothing more to send right now, clean up.
+- delete watcher;
+- }
+- break;
+- }
+- }
+- }
+-}
+-
+-
+-void sending_connect(struct ev_loop* loop, ev_io* watcher, int revents)
+-{
+- int s = watcher->fd;
+-
+- // Now check that a successful connection was made.
+- int opt;
+- socklen_t optlen = sizeof(opt);
+-
+- if (getsockopt(s, SOL_SOCKET, SO_ERROR, &opt, &optlen) < 0 || opt != 0) {
+- // Connect failure.
+- VLOG(1) << "Socket error while connecting";
+- socket_manager->close(s);
+- MessageEncoder* encoder = (MessageEncoder*) watcher->data;
+- delete encoder;
+- ev_io_stop(loop, watcher);
+- delete watcher;
+- } else {
+- // We're connected! Now let's do some sending.
+- ev_io_stop(loop, watcher);
+- ev_io_init(watcher, send_data, s, EV_WRITE);
+- ev_io_start(loop, watcher);
+- }
+-}
+-
+-
+-void receiving_connect(struct ev_loop* loop, ev_io* watcher, int revents)
+-{
+- int s = watcher->fd;
+-
+- // Now check that a successful connection was made.
+- int opt;
+- socklen_t optlen = sizeof(opt);
+-
+- if (getsockopt(s, SOL_SOCKET, SO_ERROR, &opt, &optlen) < 0 || opt != 0) {
+- // Connect failure.
+- VLOG(1) << "Socket error while connecting";
+- socket_manager->close(s);
+- DataDecoder* decoder = (DataDecoder*) watcher->data;
+- delete decoder;
+- ev_io_stop(loop, watcher);
+- delete watcher;
+- } else {
+- // We're connected! Now let's do some receiving.
+- ev_io_stop(loop, watcher);
+- ev_io_init(watcher, recv_data, s, EV_READ);
+- ev_io_start(loop, watcher);
+- }
+-}
+-
+-
+-void accept(struct ev_loop* loop, ev_io* watcher, int revents)
+-{
+- CHECK_EQ(__s__, watcher->fd);
+-
+- sockaddr_in addr;
+- socklen_t addrlen = sizeof(addr);
+-
+- int s = ::accept(__s__, (sockaddr*) &addr, &addrlen);
+-
+- if (s < 0) {
+- return;
+- }
+-
+- Try<Nothing> nonblock = os::nonblock(s);
+- if (nonblock.isError()) {
+- LOG_IF(INFO, VLOG_IS_ON(1)) << "Failed to accept, nonblock: "
+- << nonblock.error();
+- os::close(s);
+- return;
+- }
+-
+- Try<Nothing> cloexec = os::cloexec(s);
+- if (cloexec.isError()) {
+- LOG_IF(INFO, VLOG_IS_ON(1)) << "Failed to accept, cloexec: "
+- << cloexec.error();
+- os::close(s);
+- return;
+- }
+-
+- // Turn off Nagle (TCP_NODELAY) so pipelined requests don't wait.
+- int on = 1;
+- if (setsockopt(s, SOL_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) {
+- const char* error = strerror(errno);
+- VLOG(1) << "Failed to turn off the Nagle algorithm: " << error;
+- os::close(s);
+- } else {
+- // Inform the socket manager for proper bookkeeping.
+- const Socket& socket = socket_manager->accepted(s);
+-
+- // Allocate and initialize the decoder and watcher.
+- DataDecoder* decoder = new DataDecoder(socket);
+-
+- ev_io* watcher = new ev_io();
+- watcher->data = decoder;
+-
+- ev_io_init(watcher, recv_data, s, EV_READ);
+- ev_io_start(loop, watcher);
+- }
+-}
+-
+-
+-void polled(struct ev_loop* loop, ev_io* watcher, int revents)
+-{
+- Promise<short>* promise = (Promise<short>*) watcher->data;
+- promise->set(revents);
+- delete promise;
+-
+- ev_io_stop(loop, watcher);
+- delete watcher;
+-}
+-
+-
+-void* serve(void* arg)
+-{
+- ev_loop(((struct ev_loop*) arg), 0);
+-
+- return NULL;
+-}
+-
+-
+-void* schedule(void* arg)
+-{
+- do {
+- ProcessBase* process = process_manager->dequeue();
+- if (process == NULL) {
+- Gate::state_t old = gate->approach();
+- process = process_manager->dequeue();
+- if (process == NULL) {
+- gate->arrive(old); // Wait at gate if idle.
+- continue;
+- } else {
+- gate->leave();
+- }
+- }
+- process_manager->resume(process);
+- } while (true);
+-}
+-
+-
+-// We might find value in catching terminating signals at some point.
+-// However, for now, adding signal handlers freely is not allowed
+-// because they will clash with Java and Python virtual machines and
+-// causes hard to debug crashes/segfaults.
+-
+-// void sigbad(int signal, struct sigcontext *ctx)
+-// {
+-// // Pass on the signal (so that a core file is produced).
+-// struct sigaction sa;
+-// sa.sa_handler = SIG_DFL;
+-// sigemptyset(&sa.sa_mask);
+-// sa.sa_flags = 0;
+-// sigaction(signal, &sa, NULL);
+-// raise(signal);
+-// }
+-
+-
+-void initialize(const string& delegate)
+-{
+- // TODO(benh): Return an error if attempting to initialize again
+- // with a different delegate then originally specified.
+-
+- // static pthread_once_t init = PTHREAD_ONCE_INIT;
+- // pthread_once(&init, ...);
+-
+- static volatile bool initialized = false;
+- static volatile bool initializing = true;
+-
+- // Try and do the initialization or wait for it to complete.
+- if (initialized && !initializing) {
+- return;
+- } else if (initialized && initializing) {
+- while (initializing);
+- return;
+- } else {
+- if (!__sync_bool_compare_and_swap(&initialized, false, true)) {
+- while (initializing);
+- return;
+- }
+- }
+-
+-// // Install signal handler.
+-// struct sigaction sa;
+-
+-// sa.sa_handler = (void (*) (int)) sigbad;
+-// sigemptyset (&sa.sa_mask);
+-// sa.sa_flags = SA_RESTART;
+-
+-// sigaction (SIGTERM, &sa, NULL);
+-// sigaction (SIGINT, &sa, NULL);
+-// sigaction (SIGQUIT, &sa, NULL);
+-// sigaction (SIGSEGV, &sa, NULL);
+-// sigaction (SIGILL, &sa, NULL);
+-// #ifdef SIGBUS
+-// sigaction (SIGBUS, &sa, NULL);
+-// #endif
+-// #ifdef SIGSTKFLT
+-// sigaction (SIGSTKFLT, &sa, NULL);
+-// #endif
+-// sigaction (SIGABRT, &sa, NULL);
+-
+-// sigaction (SIGFPE, &sa, NULL);
+-
+-#ifdef __sun__
+- /* Need to ignore this since we can't do MSG_NOSIGNAL on Solaris. */
+- signal(SIGPIPE, SIG_IGN);
+-#endif // __sun__
+-
+- // Create a new ProcessManager and SocketManager.
+- process_manager = new ProcessManager(delegate);
+- socket_manager = new SocketManager();
+-
+- // Setup processing threads.
+- // We create no fewer than 8 threads because some tests require
+- // more worker threads than 'sysconf(_SC_NPROCESSORS_ONLN)' on
+- // computers with fewer cores.
+- // e.g. https://issues.apache.org/jira/browse/MESOS-818
+- //
+- // TODO(xujyan): Use a smarter algorithm to allocate threads.
+- // Allocating a static number of threads can cause starvation if
+- // there are more waiting Processes than the number of worker
+- // threads.
+- long cpus = std::max(8L, sysconf(_SC_NPROCESSORS_ONLN));
+-
+- for (int i = 0; i < cpus; i++) {
+- pthread_t thread; // For now, not saving handles on our threads.
+- if (pthread_create(&thread, NULL, schedule, NULL) != 0) {
+- LOG(FATAL) << "Failed to initialize, pthread_create";
+- }
+- }
+-
+- __ip__ = 0;
+- __port__ = 0;
+-
+- char* value;
+-
+- // Check environment for ip.
+- value = getenv("LIBPROCESS_IP");
+- if (value != NULL) {
+- int result = inet_pton(AF_INET, value, &__ip__);
+- if (result == 0) {
+- LOG(FATAL) << "LIBPROCESS_IP=" << value << " was unparseable";
+- } else if (result < 0) {
+- PLOG(FATAL) << "Failed to initialize, inet_pton";
+- }
+- }
+-
+- // Check environment for port.
+- value = getenv("LIBPROCESS_PORT");
+- if (value != NULL) {
+- int result = atoi(value);
+- if (result < 0 || result > USHRT_MAX) {
+- LOG(FATAL) << "LIBPROCESS_PORT=" << value << " is not a valid port";
+- }
+- __port__ = result;
+- }
+-
+- // Create a "server" socket for communicating with other nodes.
+- if ((__s__ = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+- PLOG(FATAL) << "Failed to initialize, socket";
+- }
+-
+- // Make socket non-blocking.
+- Try<Nothing> nonblock = os::nonblock(__s__);
+- if (nonblock.isError()) {
+- LOG(FATAL) << "Failed to initialize, nonblock: " << nonblock.error();
+- }
+-
+- // Set FD_CLOEXEC flag.
+- Try<Nothing> cloexec = os::cloexec(__s__);
+- if (cloexec.isError()) {
+- LOG(FATAL) << "Failed to initialize, cloexec: " << cloexec.error();
+- }
+-
+- // Allow address reuse.
+- int on = 1;
+- if (setsockopt(__s__, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
+- PLOG(FATAL) << "Failed to initialize, setsockopt(SO_REUSEADDR)";
+- }
+-
+- // Set up socket.
+- sockaddr_in addr;
+- memset(&addr, 0, sizeof(addr));
+- addr.sin_family = PF_INET;
+- addr.sin_addr.s_addr = __ip__;
+- addr.sin_port = htons(__port__);
+-
+- if (bind(__s__, (sockaddr*) &addr, sizeof(addr)) < 0) {
+- PLOG(FATAL) << "Failed to initialize, bind";
+- }
+-
+- // Lookup and store assigned ip and assigned port.
+- socklen_t addrlen = sizeof(addr);
+- if (getsockname(__s__, (sockaddr*) &addr, &addrlen) < 0) {
+- PLOG(FATAL) << "Failed to initialize, getsockname";
+- }
+-
+- __ip__ = addr.sin_addr.s_addr;
+- __port__ = ntohs(addr.sin_port);
+-
+- // Lookup hostname if missing ip or if ip is 127.0.0.1 in case we
+- // actually have a valid external ip address. Note that we need only
+- // one ip address, so that other processes can send and receive and
+- // don't get confused as to whom they are sending to.
+- if (__ip__ == 0 || __ip__ == 2130706433) {
+- char hostname[512];
+-
+- if (gethostname(hostname, sizeof(hostname)) < 0) {
+- LOG(FATAL) << "Failed to initialize, gethostname: "
+- << hstrerror(h_errno);
+- }
+-
+- // Lookup IP address of local hostname.
+- hostent* he;
+-
+- if ((he = gethostbyname2(hostname, AF_INET)) == NULL) {
+- LOG(FATAL) << "Failed to initialize, gethostbyname2: "
+- << hstrerror(h_errno);
+- }
+-
+- __ip__ = *((uint32_t *) he->h_addr_list[0]);
+- }
+-
+- if (listen(__s__, 500000) < 0) {
+- PLOG(FATAL) << "Failed to initialize, listen";
+- }
+-
+- // Setup event loop.
+-#ifdef __sun__
+- loop = ev_default_loop(EVBACKEND_POLL | EVBACKEND_SELECT);
+-#else
+- loop = ev_default_loop(EVFLAG_AUTO);
+-#endif // __sun__
+-
+- ev_async_init(&async_watcher, handle_async);
+- ev_async_start(loop, &async_watcher);
+-
+- ev_timer_init(&timeouts_watcher, handle_timeouts, 0., 2100000.0);
+- ev_timer_again(loop, &timeouts_watcher);
+-
+- ev_io_init(&server_watcher, accept, __s__, EV_READ);
+- ev_io_start(loop, &server_watcher);
+-
+-// ev_child_init(&child_watcher, child_exited, pid, 0);
+-// ev_child_start(loop, &cw);
+-
+-// /* Install signal handler. */
+-// struct sigaction sa;
+-
+-// sa.sa_handler = ev_sighandler;
+-// sigfillset (&sa.sa_mask);
+-// sa.sa_flags = SA_RESTART; /* if restarting works we save one iteration */
+-// sigaction (w->signum, &sa, 0);
+-
+-// sigemptyset (&sa.sa_mask);
+-// sigaddset (&sa.sa_mask, w->signum);
+-// sigprocmask (SIG_UNBLOCK, &sa.sa_mask, 0);
+-
+- pthread_t thread; // For now, not saving handles on our threads.
+- if (pthread_create(&thread, NULL, serve, loop) != 0) {
+- LOG(FATAL) << "Failed to initialize, pthread_create";
+- }
+-
+- // Need to set initialzing here so that we can actually invoke
+- // 'spawn' below for the garbage collector.
+- initializing = false;
+-
+- // TODO(benh): Make sure creating the garbage collector, logging
+- // process, and profiler always succeeds and use supervisors to make
+- // sure that none terminate.
+-
+- // Create global garbage collector process.
+- gc = spawn(new GarbageCollector());
+-
+- // Create global help process.
+- help = spawn(new Help(), true);
+-
+- // Create the global logging process.
+- spawn(new Logging(), true);
+-
+- // Create the global profiler process.
+- spawn(new Profiler(), true);
+-
+- // Create the global statistics.
+- value = getenv("LIBPROCESS_STATISTICS_WINDOW");
+- if (value != NULL) {
+- Try<Duration> window = Duration::parse(string(value));
+- if (window.isError()) {
+- LOG(FATAL) << "LIBPROCESS_STATISTICS_WINDOW=" << value
+- << " is not a valid duration: " << window.error();
+- }
+- statistics = new Statistics(window.get());
+- } else {
+- // TODO(bmahler): Investigate memory implications of this window
+- // size. We may also want to provide a maximum memory size rather than
+- // time window. Or, offload older data to disk, etc.
+- statistics = new Statistics(LIBPROCESS_STATISTICS_WINDOW);
+- }
+-
+- // Initialize the mime types.
+- mime::initialize();
+-
+- // Initialize the response statuses.
+- http::initialize();
+-
+- // Add a route for getting process information.
+- lambda::function<Future<Response>(const Request&)> __processes__ =
+- lambda::bind(&ProcessManager::__processes__, process_manager, lambda::_1);
+-
+- new Route("/__processes__", None(), __processes__);
+-
+- char temp[INET_ADDRSTRLEN];
+- if (inet_ntop(AF_INET, (in_addr*) &__ip__, temp, INET_ADDRSTRLEN) == NULL) {
+- PLOG(FATAL) << "Failed to initialize, inet_ntop";
+- }
+-
+- VLOG(1) << "libprocess is initialized on " << temp << ":" << __port__
+- << " for " << cpus << " cpus";
+-}
+-
+-
+-uint32_t ip()
+-{
+- process::initialize();
+- return __ip__;
+-}
+-
+-
+-uint16_t port()
+-{
+- process::initialize();
+- return __port__;
+-}
+-
+-
+-HttpProxy::HttpProxy(const Socket& _socket)
+- : ProcessBase(ID::generate("__http__")),
+- socket(_socket) {}
+-
+-
+-HttpProxy::~HttpProxy()
+-{
+- // Need to make sure response producers know not to continue to
+- // create a response (streaming or otherwise).
+- if (pipe.isSome()) {
+- os::close(pipe.get());
+- }
+- pipe = None();
+-
+- while (!items.empty()) {
+- Item* item = items.front();
+-
+- // Attempt to discard the future.
+- item->future->discard();
+-
+- // But it might have already been ready ...
+- if (item->future->isReady()) {
+- const Response& response = item->future->get();
+- if (response.type == Response::PIPE) {
+- os::close(response.pipe);
+- }
+- }
+-
+- items.pop();
+- delete item;
+- }
+-}
+-
+-
+-void HttpProxy::enqueue(const Response& response, const Request& request)
+-{
+- handle(new Future<Response>(response), request);
+-}
+-
+-
+-void HttpProxy::handle(Future<Response>* future, const Request& request)
+-{
+- items.push(new Item(request, future));
+-
+- if (items.size() == 1) {
+- next();
+- }
+-}
+-
+-
+-void HttpProxy::next()
+-{
+- if (items.size() > 0) {
+- // Wait for any transition of the future.
+- items.front()->future->onAny(
+- defer(self(), &HttpProxy::waited, lambda::_1));
+- }
+-}
+-
+-
+-void HttpProxy::waited(const Future<Response>& future)
+-{
+- CHECK(items.size() > 0);
+- Item* item = items.front();
+-
+- CHECK(future == *item->future);
+-
+- // Process the item and determine if we're done or not (so we know
+- // whether to start waiting on the next responses).
+- bool processed = process(*item->future, item->request);
+-
+- items.pop();
+- delete item;
+-
+- if (processed) {
+- next();
+- }
+-}
+-
+-
+-bool HttpProxy::process(const Future<Response>& future, const Request& request)
+-{
+- if (!future.isReady()) {
+- // TODO(benh): Consider handling other "states" of future
+- // (discarded, failed, etc) with different HTTP statuses.
+- socket_manager->send(ServiceUnavailable(), request, socket);
+- return true; // All done, can process next response.
+- }
+-
+- Response response = future.get();
+-
+- // If the response specifies a path, try and perform a sendfile.
+- if (response.type == Response::PATH) {
+- // Make sure no body is sent (this is really an error and
+- // should be reported and no response sent.
+- response.body.clear();
+-
+- const string& path = response.path;
+- int fd = open(path.c_str(), O_RDONLY);
+- if (fd < 0) {
+- if (errno == ENOENT || errno == ENOTDIR) {
+- VLOG(1) << "Returning '404 Not Found' for path '" << path << "'";
+- socket_manager->send(NotFound(), request, socket);
+- } else {
+- const char* error = strerror(errno);
+- VLOG(1) << "Failed to send file at '" << path << "': " << error;
+- socket_manager->send(InternalServerError(), request, socket);
+- }
+- } else {
+- struct stat s; // Need 'struct' because of function named 'stat'.
+- if (fstat(fd, &s) != 0) {
+- const char* error = strerror(errno);
+- VLOG(1) << "Failed to send file at '" << path << "': " << error;
+- socket_manager->send(InternalServerError(), request, socket);
+- } else if (S_ISDIR(s.st_mode)) {
+- VLOG(1) << "Returning '404 Not Found' for directory '" << path << "'";
+- socket_manager->send(NotFound(), request, socket);
+- } else {
+- // While the user is expected to properly set a 'Content-Type'
+- // header, we fill in (or overwrite) 'Content-Length' header.
+- stringstream out;
+- out << s.st_size;
+- response.headers["Content-Length"] = out.str();
+-
+- if (s.st_size == 0) {
+- socket_manager->send(response, request, socket);
+- return true; // All done, can process next request.
+- }
+-
+- VLOG(1) << "Sending file at '" << path << "' with length " << s.st_size;
+-
+- // TODO(benh): Consider a way to have the socket manager turn
+- // on TCP_CORK for both sends and then turn it off.
+- socket_manager->send(
+- new HttpResponseEncoder(socket, response, request),
+- true);
+-
+- // Note the file descriptor gets closed by FileEncoder.
+- socket_manager->send(
+- new FileEncoder(socket, fd, s.st_size),
+- request.keepAlive);
+- }
+- }
+- } else if (response.type == Response::PIPE) {
+- // Make sure no body is sent (this is really an error and
+- // should be reported and no response sent.
+- response.body.clear();
+-
+- // Make sure the pipe is nonblocking.
+- Try<Nothing> nonblock = os::nonblock(response.pipe);
+- if (nonblock.isError()) {
+- const char* error = strerror(errno);
+- VLOG(1) << "Failed make pipe nonblocking: " << error;
+- socket_manager->send(InternalServerError(), request, socket);
+- return true; // All done, can process next response.
+- }
+-
+- // While the user is expected to properly set a 'Content-Type'
+- // header, we fill in (or overwrite) 'Transfer-Encoding' header.
+- response.headers["Transfer-Encoding"] = "chunked";
+-
+- VLOG(1) << "Starting \"chunked\" streaming";
+-
+- socket_manager->send(
+- new HttpResponseEncoder(socket, response, request),
+- true);
+-
+- pipe = response.pipe;
+-
+- io::poll(pipe.get(), io::READ).onAny(
+- defer(self(), &Self::stream, lambda::_1, request));
+-
+- return false; // Streaming, don't process next response (yet)!
+- } else {
+- socket_manager->send(response, request, socket);
+- }
+-
+- return true; // All done, can process next response.
+-}
+-
+-
+-void HttpProxy::stream(const Future<short>& poll, const Request& request)
+-{
+- // TODO(benh): Use 'splice' on Linux.
+-
+- CHECK(pipe.isSome());
+-
+- bool finished = false; // Whether we're done streaming.
+-
+- if (poll.isReady()) {
+- // Read and write.
+- CHECK(poll.get() == io::READ);
+- const size_t size = 4 * 1024; // 4K.
+- char data[size];
+- while (!finished) {
+- ssize_t length = ::read(pipe.get(), data, size);
+- if (length < 0 && (errno == EINTR)) {
+- // Interrupted, try again now.
+- continue;
+- } else if (length < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+- // Might block, try again later.
+- io::poll(pipe.get(), io::READ).onAny(
+- defer(self(), &Self::stream, lambda::_1, request));
+- break;
+- } else {
+- std::ostringstream out;
+- if (length <= 0) {
+- // Error or closed, treat both as closed.
+- if (length < 0) {
+- // Error.
+- const char* error = strerror(errno);
+- VLOG(1) << "Read error while streaming: " << error;
+- }
+- out << "0\r\n" << "\r\n";
+- finished = true;
+- } else {
+- // Data!
+- out << std::hex << length << "\r\n";
+- out.write(data, length);
+- out << "\r\n";
+- }
+-
+- // We always persist the connection when we're not finished
+- // streaming.
+- socket_manager->send(
+- new DataEncoder(socket, out.str()),
+- finished ? request.keepAlive : true);
+- }
+- }
+- } else if (poll.isFailed()) {
+- VLOG(1) << "Failed to poll: " << poll.failure();
+- socket_manager->send(InternalServerError(), request, socket);
+- finished = true;
+- } else {
+- VLOG(1) << "Unexpected discarded future while polling";
+- socket_manager->send(InternalServerError(), request, socket);
+- finished = true;
+- }
+-
+- if (finished) {
+- os::close(pipe.get());
+- pipe = None();
+- next();
+- }
+-}
+-
+-
+-SocketManager::SocketManager()
+-{
+- synchronizer(this) = SYNCHRONIZED_INITIALIZER_RECURSIVE;
+-}
+-
+-
+-SocketManager::~SocketManager() {}
+-
+-
+-Socket SocketManager::accepted(int s)
+-{
+- synchronized (this) {
+- return sockets[s] = Socket(s);
+- }
+-}
+-
+-
+-void SocketManager::link(ProcessBase* process, const UPID& to)
+-{
+- // TODO(benh): The semantics we want to support for link are such
+- // that if there is nobody to link to (local or remote) then an
+- // ExitedEvent gets generated. This functionality has only been
+- // implemented when the link is local, not remote. Of course, if
+- // there is nobody listening on the remote side, then this should
+- // work remotely ... but if there is someone listening remotely just
+- // not at that id, then it will silently continue executing.
+-
+- CHECK(process != NULL);
+-
+- Node node(to.ip, to.port);
+-
+- synchronized (this) {
+- // Check if node is remote and there isn't a persistant link.
+- if ((node.ip != __ip__ || node.port != __port__)
+- && persists.count(node) == 0) {
+- // Okay, no link, lets create a socket.
+- int s;
+-
+- if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+- PLOG(FATAL) << "Failed to link, socket";
+- }
+-
+- Try<Nothing> nonblock = os::nonblock(s);
+- if (nonblock.isError()) {
+- LOG(FATAL) << "Failed to link, nonblock: " << nonblock.error();
+- }
+-
+- Try<Nothing> cloexec = os::cloexec(s);
+- if (cloexec.isError()) {
+- LOG(FATAL) << "Failed to link, cloexec: " << cloexec.error();
+- }
+-
+- sockets[s] = Socket(s);
+- nodes[s] = node;
+-
+- persists[node] = s;
+-
+- // Allocate and initialize the decoder and watcher (we really
+- // only "receive" on this socket so that we can react when it
+- // gets closed and generate appropriate lost events).
+- DataDecoder* decoder = new DataDecoder(sockets[s]);
+-
+- ev_io* watcher = new ev_io();
+- watcher->data = decoder;
+-
+- // Try and connect to the node using this socket.
+- sockaddr_in addr;
+- memset(&addr, 0, sizeof(addr));
+- addr.sin_family = PF_INET;
+- addr.sin_port = htons(to.port);
+- addr.sin_addr.s_addr = to.ip;
+-
+- if (connect(s, (sockaddr*) &addr, sizeof(addr)) < 0) {
+- if (errno != EINPROGRESS) {
+- PLOG(FATAL) << "Failed to link, connect";
+- }
+-
+- // Wait for socket to be connected.
+- ev_io_init(watcher, receiving_connect, s, EV_WRITE);
+- } else {
+- ev_io_init(watcher, recv_data, s, EV_READ);
+- }
+-
+- // Enqueue the watcher.
+- synchronized (watchers) {
+- watchers->push(watcher);
+- }
+-
+- // Interrupt the loop.
+- ev_async_send(loop, &async_watcher);
+- }
+-
+- links[to].insert(process);
+- }
+-}
+-
+-
+-PID<HttpProxy> SocketManager::proxy(const Socket& socket)
+-{
+- HttpProxy* proxy = NULL;
+-
+- synchronized (this) {
+- // This socket might have been asked to get closed (e.g., remote
+- // side hang up) while a process is attempting to handle an HTTP
+- // request. Thus, if there is no more socket, return an empty PID.
+- if (sockets.count(socket) > 0) {
+- if (proxies.count(socket) > 0) {
+- return proxies[socket]->self();
+- } else {
+- proxy = new HttpProxy(sockets[socket]);
+- proxies[socket] = proxy;
+- }
+- }
+- }
+-
+- // Now check if we need to spawn a newly created proxy. Note that we
+- // need to do this outside of the synchronized block above to avoid
+- // a possible deadlock (because spawn eventually synchronizes on
+- // ProcessManager and ProcessManager::cleanup synchronizes on
+- // ProcessManager and then SocketManager, so a deadlock results if
+- // we do spawn within the synchronized block above).
+- if (proxy != NULL) {
+- return spawn(proxy, true);
+- }
+-
+- return PID<HttpProxy>();
+-}
+-
+-
+-void SocketManager::send(Encoder* encoder, bool persist)
+-{
+- CHECK(encoder != NULL);
+-
+- synchronized (this) {
+- if (sockets.count(encoder->socket()) > 0) {
+- // Update whether or not this socket should get disposed after
+- // there is no more data to send.
+- if (!persist) {
+- dispose.insert(encoder->socket());
+- }
+-
+- if (outgoing.count(encoder->socket()) > 0) {
+- outgoing[encoder->socket()].push(encoder);
+- } else {
+- // Initialize the outgoing queue.
+- outgoing[encoder->socket()];
+-
+- // Allocate and initialize the watcher.
+- ev_io* watcher = new ev_io();
+- watcher->data = encoder;
+-
+- ev_io_init(watcher, encoder->sender(), encoder->socket(), EV_WRITE);
+-
+- synchronized (watchers) {
+- watchers->push(watcher);
+- }
+-
+- ev_async_send(loop, &async_watcher);
+- }
+- } else {
+- VLOG(1) << "Attempting to send on a no longer valid socket!";
+- delete encoder;
+- }
+- }
+-}
+-
+-
+-void SocketManager::send(
+- const Response& response,
+- const Request& request,
+- const Socket& socket)
+-{
+- bool persist = request.keepAlive;
+-
+- // Don't persist the connection if the headers include
+- // 'Connection: close'.
+- if (response.headers.contains("Connection")) {
+- if (response.headers.get("Connection").get() == "close") {
+- persist = false;
+- }
+- }
+-
+- send(new HttpResponseEncoder(socket, response, request), persist);
+-}
+-
+-
+-void SocketManager::send(Message* message)
+-{
+- CHECK(message != NULL);
+-
+- Node node(message->to.ip, message->to.port);
+-
+- synchronized (this) {
+- // Check if there is already a socket.
+- bool persist = persists.count(node) > 0;
+- bool temp = temps.count(node) > 0;
+- if (persist || temp) {
+- int s = persist ? persists[node] : temps[node];
+- CHECK(sockets.count(s) > 0);
+- send(new MessageEncoder(sockets[s], message), persist);
+- } else {
+- // No peristant or temporary socket to the node currently
+- // exists, so we create a temporary one.
+- int s;
+-
+- if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+- PLOG(FATAL) << "Failed to send, socket";
+- }
+-
+- Try<Nothing> nonblock = os::nonblock(s);
+- if (nonblock.isError()) {
+- LOG(FATAL) << "Failed to send, nonblock: " << nonblock.error();
+- }
+-
+- Try<Nothing> cloexec = os::cloexec(s);
+- if (cloexec.isError()) {
+- LOG(FATAL) << "Failed to send, cloexec: " << cloexec.error();
+- }
+-
+- sockets[s] = Socket(s);
+- nodes[s] = node;
+- temps[node] = s;
+-
+- dispose.insert(s);
+-
+- // Initialize the outgoing queue.
+- outgoing[s];
+-
+- // Allocate and initialize the watcher.
+- ev_io* watcher = new ev_io();
+- watcher->data = new MessageEncoder(sockets[s], message);
+-
+- // Try and connect to the node using this socket.
+- sockaddr_in addr;
+- memset(&addr, 0, sizeof(addr));
+- addr.sin_family = PF_INET;
+- addr.sin_port = htons(message->to.port);
+- addr.sin_addr.s_addr = message->to.ip;
+-
+- if (connect(s, (sockaddr*) &addr, sizeof(addr)) < 0) {
+- if (errno != EINPROGRESS) {
+- PLOG(FATAL) << "Failed to send, connect";
+- }
+-
+- // Initialize watcher for connecting.
+- ev_io_init(watcher, sending_connect, s, EV_WRITE);
+- } else {
+- // Initialize watcher for sending.
+- ev_io_init(watcher, send_data, s, EV_WRITE);
+- }
+-
+- // Enqueue the watcher.
+- synchronized (watchers) {
+- watchers->push(watcher);
+- }
+-
+- ev_async_send(loop, &async_watcher);
+- }
+- }
+-}
+-
+-
+-Encoder* SocketManager::next(int s)
+-{
+- HttpProxy* proxy = NULL; // Non-null if needs to be terminated.
+-
+- synchronized (this) {
+- // We cannot assume 'sockets.count(s) > 0' here because it's
+- // possible that 's' has been removed with a a call to
+- // SocketManager::close. For example, it could be the case that a
+- // socket has gone to CLOSE_WAIT and the call to 'recv' in
+- // recv_data returned 0 causing SocketManager::close to get
+- // invoked. Later a call to 'send' or 'sendfile' (e.g., in
+- // send_data or send_file) can "succeed" (because the socket is
+- // not "closed" yet because there are still some Socket
+- // references, namely the reference being used in send_data or
+- // send_file!). However, when SocketManger::next is actually
+- // invoked we find out there there is no more data and thus stop
+- // sending.
+- // TODO(benh): Should we actually finish sending the data!?
+- if (sockets.count(s) > 0) {
+- CHECK(outgoing.count(s) > 0);
+-
+- if (!outgoing[s].empty()) {
+- // More messages!
+- Encoder* encoder = outgoing[s].front();
+- outgoing[s].pop();
+- return encoder;
+- } else {
+- // No more messages ... erase the outgoing queue.
+- outgoing.erase(s);
+-
+- if (dispose.count(s) > 0) {
+- // This is either a temporary socket we created or it's a
+- // socket that we were receiving data from and possibly
+- // sending HTTP responses back on. Clean up either way.
+- if (nodes.count(s) > 0) {
+- const Node& node = nodes[s];
+- CHECK(temps.count(node) > 0 && temps[node] == s);
+- temps.erase(node);
+- nodes.erase(s);
+- }
+-
+- if (proxies.count(s) > 0) {
+- proxy = proxies[s];
+- proxies.erase(s);
+- }
+-
+- dispose.erase(s);
+- sockets.erase(s);
+-
+- // We don't actually close the socket (we wait for the Socket
+- // abstraction to close it once there are no more references),
+- // but we do shutdown the receiving end so any DataDecoder
+- // will get cleaned up (which might have the last reference).
+- shutdown(s, SHUT_RD);
+- }
+- }
+- }
+- }
+-
+- // We terminate the proxy outside the synchronized block to avoid
+- // possible deadlock between the ProcessManager and SocketManager
+- // (see comment in SocketManager::proxy for more information).
+- if (proxy != NULL) {
+- terminate(proxy);
+- }
+-
+- return NULL;
+-}
+-
+-
+-void SocketManager::close(int s)
+-{
+- HttpProxy* proxy = NULL; // Non-null if needs to be terminated.
+-
+- synchronized (this) {
+- // This socket might not be active if it was already asked to get
+- // closed (e.g., a write on the socket failed so we try and close
+- // it and then later the read side of the socket gets closed so we
+- // try and close it again). Thus, ignore the request if we don't
+- // know about the socket.
+- if (sockets.count(s) > 0) {
+- // Clean up any remaining encoders for this socket.
+- if (outgoing.count(s) > 0) {
+- while (!outgoing[s].empty()) {
+- Encoder* encoder = outgoing[s].front();
+- delete encoder;
+- outgoing[s].pop();
+- }
+-
+- outgoing.erase(s);
+- }
+-
+- // Clean up after sockets used for node communication.
+- if (nodes.count(s) > 0) {
+- const Node& node = nodes[s];
+-
+- // Don't bother invoking exited unless socket was persistant.
+- if (persists.count(node) > 0 && persists[node] == s) {
+- persists.erase(node);
+- exited(node); // Generate ExitedEvent(s)!
+- } else if (temps.count(node) > 0 && temps[node] == s) {
+- temps.erase(node);
+- }
+-
+- nodes.erase(s);
+- }
+-
+- // Clean up any proxy associated with this socket.
+- if (proxies.count(s) > 0) {
+- proxy = proxies[s];
+- proxies.erase(s);
+- }
+-
+- dispose.erase(s);
+- sockets.erase(s);
+- }
+- }
+-
+- // We terminate the proxy outside the synchronized block to avoid
+- // possible deadlock between the ProcessManager and SocketManager.
+- if (proxy != NULL) {
+- terminate(proxy);
+- }
+-
+- // Note that we don't actually:
+- //
+- // close(s);
+- //
+- // Because, for example, there could be a race between an HttpProxy
+- // trying to do send a response with SocketManager::send() or a
+- // process might be responding to another Request (e.g., trying
+- // to do a sendfile) since these things may be happening
+- // asynchronously we can't close the socket yet, because it might
+- // get reused before any of the above things have finished, and then
+- // we'll end up sending data on the wrong socket! Instead, we rely
+- // on the last reference of our Socket object to close the
+- // socket. Note, however, that since socket is no longer in
+- // 'sockets' any attempt to send with it will just get ignored.
+-}
+-
+-
+-void SocketManager::exited(const Node& node)
+-{
+- // TODO(benh): It would be cleaner if this routine could call back
+- // into ProcessManager ... then we wouldn't have to convince
+- // ourselves that the accesses to each Process object will always be
+- // valid.
+- synchronized (this) {
+- list<UPID> removed;
+- // Look up all linked processes.
+- foreachpair (const UPID& linkee, set<ProcessBase*>& processes, links) {
+- if (linkee.ip == node.ip && linkee.port == node.port) {
+- foreach (ProcessBase* linker, processes) {
+- linker->enqueue(new ExitedEvent(linkee));
+- }
+- removed.push_back(linkee);
+- }
+- }
+-
+- foreach (const UPID& pid, removed) {
+- links.erase(pid);
+- }
+- }
+-}
+-
+-
+-void SocketManager::exited(ProcessBase* process)
+-{
+- // An exited event is enough to cause the process to get deleted
+- // (e.g., by the garbage collector), which means we can't
+- // dereference process (or even use the address) after we enqueue at
+- // least one exited event. Thus, we save the process pid.
+- const UPID pid = process->pid;
+-
+- // Likewise, we need to save the current time of the process so we
+- // can update the clocks of linked processes as appropriate.
+- const Time time = Clock::now(process);
+-
+- synchronized (this) {
+- // Iterate through the links, removing any links the process might
+- // have had and creating exited events for any linked processes.
+- foreachpair (const UPID& linkee, set<ProcessBase*>& processes, links) {
+- processes.erase(process);
+-
+- if (linkee == pid) {
+- foreach (ProcessBase* linker, processes) {
+- CHECK(linker != process) << "Process linked with itself";
+- synchronized (timeouts) {
+- if (Clock::paused()) {
+- Clock::update(linker, time);
+- }
+- }
+- linker->enqueue(new ExitedEvent(linkee));
+- }
+- }
+- }
+-
+- links.erase(pid);
+- }
+-}
+-
+-
+-ProcessManager::ProcessManager(const string& _delegate)
+- : delegate(_delegate)
+-{
+- synchronizer(processes) = SYNCHRONIZED_INITIALIZER_RECURSIVE;
+- synchronizer(runq) = SYNCHRONIZED_INITIALIZER_RECURSIVE;
+- running = 0;
+- __sync_synchronize(); // Ensure write to 'running' visible in other threads.
+-}
+-
+-
+-ProcessManager::~ProcessManager() {}
+-
+-
+-ProcessReference ProcessManager::use(const UPID& pid)
+-{
+- if (pid.ip == __ip__ && pid.port == __port__) {
+- synchronized (processes) {
+- if (processes.count(pid.id) > 0) {
+- // Note that the ProcessReference constructor _must_ get
+- // called while holding the lock on processes so that waiting
+- // for references is atomic (i.e., race free).
+- return ProcessReference(processes[pid.id]);
+- }
+- }
+- }
+-
+- return ProcessReference(NULL);
+-}
+-
+-
+-bool ProcessManager::handle(
+- const Socket& socket,
+- Request* request)
+-{
+- CHECK(request != NULL);
+-
+- // Check if this is a libprocess request (i.e., 'User-Agent:
+- // libprocess/id at ip:port') and if so, parse as a message.
+- if (libprocess(request)) {
+- Message* message = parse(request);
+- if (message != NULL) {
+- delete request;
+- // TODO(benh): Use the sender PID in order to capture
+- // happens-before timing relationships for testing.
+- return deliver(message->to, new MessageEvent(message));
+- }
+-
+- VLOG(1) << "Failed to handle libprocess request: "
+- << request->method << " " << request->path
+- << " (User-Agent: " << request->headers["User-Agent"] << ")";
+-
+- delete request;
+- return false;
+- }
+-
+- // Treat this as an HTTP request. Start by checking that the path
+- // starts with a '/' (since the code below assumes as much).
+- if (request->path.find('/') != 0) {
+- VLOG(1) << "Returning '400 Bad Request' for '" << request->path << "'";
+-
+- // Get the HttpProxy pid for this socket.
+- PID<HttpProxy> proxy = socket_manager->proxy(socket);
+-
+- // Enqueue the response with the HttpProxy so that it respects the
+- // order of requests to account for HTTP/1.1 pipelining.
+- dispatch(proxy, &HttpProxy::enqueue, BadRequest(), *request);
+-
+- // Cleanup request.
+- delete request;
+- return false;
+- }
+-
+- // Ignore requests with relative paths (i.e., contain "/..").
+- if (request->path.find("/..") != string::npos) {
+- VLOG(1) << "Returning '404 Not Found' for '" << request->path
+- << "' (ignoring requests with relative paths)";
+-
+- // Get the HttpProxy pid for this socket.
+- PID<HttpProxy> proxy = socket_manager->proxy(socket);
+-
+- // Enqueue the response with the HttpProxy so that it respects the
+- // order of requests to account for HTTP/1.1 pipelining.
+- dispatch(proxy, &HttpProxy::enqueue, NotFound(), *request);
+-
+- // Cleanup request.
+- delete request;
+- return false;
+- }
+-
+- // Split the path by '/'.
+- vector<string> tokens = strings::tokenize(request->path, "/");
+-
+- // Try and determine a receiver, otherwise try and delegate.
+- ProcessReference receiver;
+-
+- if (tokens.size() == 0 && delegate != "") {
+- request->path = "/" + delegate;
+- receiver = use(UPID(delegate, __ip__, __port__));
+- } else if (tokens.size() > 0) {
+- receiver = use(UPID(tokens[0], __ip__, __port__));
+- }
+-
+- if (!receiver && delegate != "") {
+- // Try and delegate the request.
+- request->path = "/" + delegate + request->path;
+- receiver = use(UPID(delegate, __ip__, __port__));
+- }
+-
+- if (receiver) {
+- // TODO(benh): Use the sender PID in order to capture
+- // happens-before timing relationships for testing.
+- return deliver(receiver, new HttpEvent(socket, request));
+- }
+-
+- // This has no receiver, send error response.
+- VLOG(1) << "Returning '404 Not Found' for '" << request->path << "'";
+-
+- // Get the HttpProxy pid for this socket.
+- PID<HttpProxy> proxy = socket_manager->proxy(socket);
+-
+- // Enqueue the response with the HttpProxy so that it respects the
+- // order of requests to account for HTTP/1.1 pipelining.
+- dispatch(proxy, &HttpProxy::enqueue, NotFound(), *request);
+-
+- // Cleanup request.
+- delete request;
+- return false;
+-}
+-
+-
+-bool ProcessManager::deliver(
+- ProcessBase* receiver,
+- Event* event,
+- ProcessBase* sender)
+-{
+- CHECK(event != NULL);
+-
+- // If we are using a manual clock then update the current time of
+- // the receiver using the sender if necessary to preserve the
+- // happens-before relationship between the sender and receiver. Note
+- // that the assumption is that the sender remains valid for at least
+- // the duration of this routine (so that we can look up it's current
+- // time).
+- if (Clock::paused()) {
+- synchronized (timeouts) {
+- if (Clock::paused()) {
+- if (sender != NULL) {
+- Clock::order(sender, receiver);
+- } else {
+- Clock::update(receiver, Clock::now());
+- }
+- }
+- }
+- }
+-
+- receiver->enqueue(event);
+-
+- return true;
+-}
+-
+-
+-bool ProcessManager::deliver(
+- const UPID& to,
+- Event* event,
+- ProcessBase* sender)
+-{
+- CHECK(event != NULL);
+-
+- if (ProcessReference receiver = use(to)) {
+- return deliver(receiver, event, sender);
+- }
+-
+- delete event;
+- return false;
+-}
+-
+-
+-UPID ProcessManager::spawn(ProcessBase* process, bool manage)
+-{
+- CHECK(process != NULL);
+-
+- synchronized (processes) {
+- if (processes.count(process->pid.id) > 0) {
+- return UPID();
+- } else {
+- processes[process->pid.id] = process;
+- }
+- }
+-
+- // Use the garbage collector if requested.
+- if (manage) {
+- dispatch(gc, &GarbageCollector::manage<ProcessBase>, process);
+- }
+-
+- // We save the PID before enqueueing the process to avoid the race
+- // condition that occurs when a user has a very short process and
+- // the process gets run and cleaned up before we return from enqueue
+- // (e.g., when 'manage' is set to true).
+- UPID pid = process->self();
+-
+- // Add process to the run queue (so 'initialize' will get invoked).
+- enqueue(process);
+-
+- VLOG(2) << "Spawned process " << pid;
+-
+- return pid;
+-}
+-
+-
+-void ProcessManager::resume(ProcessBase* process)
+-{
+- __process__ = process;
+-
+- VLOG(2) << "Resuming " << process->pid << " at " << Clock::now();
+-
+- bool terminate = false;
+- bool blocked = false;
+-
+- CHECK(process->state == ProcessBase::BOTTOM ||
+- process->state == ProcessBase::READY);
+-
+- if (process->state == ProcessBase::BOTTOM) {
+- process->state = ProcessBase::RUNNING;
+- try { process->initialize(); }
+- catch (...) { terminate = true; }
+- }
+-
+- while (!terminate && !blocked) {
+- Event* event = NULL;
+-
+- process->lock();
+- {
+- if (process->events.size() > 0) {
+- event = process->events.front();
+- process->events.pop_front();
+- process->state = ProcessBase::RUNNING;
+- } else {
+- process->state = ProcessBase::BLOCKED;
+- blocked = true;
+- }
+- }
+- process->unlock();
+-
+- if (!blocked) {
+- CHECK(event != NULL);
+-
+- // Determine if we should filter this event.
+- synchronized (filterer) {
+- if (filterer != NULL) {
+- bool filter = false;
+- struct FilterVisitor : EventVisitor
+- {
+- FilterVisitor(bool* _filter) : filter(_filter) {}
+-
+- virtual void visit(const MessageEvent& event)
+- {
+- *filter = filterer->filter(event);
+- }
+-
+- virtual void visit(const DispatchEvent& event)
+- {
+- *filter = filterer->filter(event);
+- }
+-
+- virtual void visit(const HttpEvent& event)
+- {
+- *filter = filterer->filter(event);
+- }
+-
+- virtual void visit(const ExitedEvent& event)
+- {
+- *filter = filterer->filter(event);
+- }
+-
+- bool* filter;
+- } visitor(&filter);
+-
+- event->visit(&visitor);
+-
+- if (filter) {
+- delete event;
+- continue; // Try and execute the next event.
+- }
+- }
+- }
+-
+- // Determine if we should terminate.
+- terminate = event->is<TerminateEvent>();
+-
+- // Now service the event.
+- try {
+- process->serve(*event);
+- } catch (const std::exception& e) {
+- std::cerr << "libprocess: " << process->pid
+- << " terminating due to "
+- << e.what() << std::endl;
+- terminate = true;
+- } catch (...) {
+- std::cerr << "libprocess: " << process->pid
+- << " terminating due to unknown exception" << std::endl;
+- terminate = true;
+- }
+-
+- delete event;
+-
+- if (terminate) {
+- cleanup(process);
+- }
+- }
+- }
+-
+- __process__ = NULL;
+-
+- CHECK_GE(running, 1);
+- __sync_fetch_and_sub(&running, 1);
+-}
+-
+-
+-void ProcessManager::cleanup(ProcessBase* process)
+-{
+- VLOG(2) << "Cleaning up " << process->pid;
+-
+- // First, set the terminating state so no more events will get
+- // enqueued and delete al the pending events. We want to delete the
+- // events before we hold the processes lock because deleting an
+- // event could cause code outside libprocess to get executed which
+- // might cause a deadlock with the processes lock. Likewise,
+- // deleting the events now rather than later has the nice property
+- // of making sure that any events that might have gotten enqueued on
+- // the process we are cleaning up will get dropped (since it's
+- // terminating) and eliminates the potential of enqueueing them on
+- // another process that gets spawned with the same PID.
+- deque<Event*> events;
+-
+- process->lock();
+- {
+- process->state = ProcessBase::TERMINATING;
+- events = process->events;
+- process->events.clear();
+- }
+- process->unlock();
+-
+- // Delete pending events.
+- while (!events.empty()) {
+- Event* event = events.front();
+- events.pop_front();
+- delete event;
+- }
+-
+- // Possible gate non-libprocess threads are waiting at.
+- Gate* gate = NULL;
+-
+- // Remove process.
+- synchronized (processes) {
+- // Wait for all process references to get cleaned up.
+- while (process->refs > 0) {
+- asm ("pause");
+- __sync_synchronize();
+- }
+-
+- process->lock();
+- {
+- CHECK(process->events.empty());
+-
+- processes.erase(process->pid.id);
+-
+- // Lookup gate to wake up waiting threads.
+- map<ProcessBase*, Gate*>::iterator it = gates.find(process);
+- if (it != gates.end()) {
+- gate = it->second;
+- // N.B. The last thread that leaves the gate also free's it.
+- gates.erase(it);
+- }
+-
+- CHECK(process->refs == 0);
+- process->state = ProcessBase::TERMINATED;
+- }
+- process->unlock();
+-
+- // Note that we don't remove the process from the clock during
+- // cleanup, but rather the clock is reset for a process when it is
+- // created (see ProcessBase::ProcessBase). We do this so that
+- // SocketManager::exited can access the current time of the
+- // process to "order" exited events. TODO(benh): It might make
+- // sense to consider storing the time of the process as a field of
+- // the class instead.
+-
+- // Now we tell the socket manager about this process exiting so
+- // that it can create exited events for linked processes. We
+- // _must_ do this while synchronized on processes because
+- // otherwise another process could attempt to link this process
+- // and SocketManger::link would see that the processes doesn't
+- // exist when it attempts to get a ProcessReference (since we
+- // removed the process above) thus causing an exited event, which
+- // could cause the process to get deleted (e.g., the garbage
+- // collector might link _after_ the process has already been
+- // removed from processes thus getting an exited event but we
+- // don't want that exited event to fire and actually delete the
+- // process until after we have used the process in
+- // SocketManager::exited).
+- socket_manager->exited(process);
+-
+- // ***************************************************************
+- // At this point we can no longer dereference the process since it
+- // might already be deallocated (e.g., by the garbage collector).
+- // ***************************************************************
+-
+- // Note that we need to open the gate while synchronized on
+- // processes because otherwise we might _open_ the gate before
+- // another thread _approaches_ the gate causing that thread to
+- // wait on _arrival_ to the gate forever (see
+- // ProcessManager::wait).
+- if (gate != NULL) {
+- gate->open();
+- }
+- }
+-}
+-
+-
+-void ProcessManager::link(ProcessBase* process, const UPID& to)
+-{
+- // Check if the pid is local.
+- if (!(to.ip == __ip__ && to.port == __port__)) {
+- socket_manager->link(process, to);
+- } else {
+- // Since the pid is local we want to get a reference to it's
+- // underlying process so that while we are invoking the link
+- // manager we don't miss sending a possible ExitedEvent.
+- if (ProcessReference _ = use(to)) {
+- socket_manager->link(process, to);
+- } else {
+- // Since the pid isn't valid it's process must have already died
+- // (or hasn't been spawned yet) so send a process exit message.
+- process->enqueue(new ExitedEvent(to));
+- }
+- }
+-}
+-
+-
+-void ProcessManager::terminate(
+- const UPID& pid,
+- bool inject,
+- ProcessBase* sender)
+-{
+- if (ProcessReference process = use(pid)) {
+- if (Clock::paused()) {
+- synchronized (timeouts) {
+- if (Clock::paused()) {
+- if (sender != NULL) {
+- Clock::order(sender, process);
+- } else {
+- Clock::update(process, Clock::now());
+- }
+- }
+- }
+- }
+-
+- if (sender != NULL) {
+- process->enqueue(new TerminateEvent(sender->self()), inject);
+- } else {
+- process->enqueue(new TerminateEvent(UPID()), inject);
+- }
+- }
+-}
+-
+-
+-bool ProcessManager::wait(const UPID& pid)
+-{
+- // We use a gate for waiters. A gate is single use. That is, a new
+- // gate is created when the first thread shows up and wants to wait
+- // for a process that currently has no gate. Once that process
+- // exits, the last thread to leave the gate will also clean it
+- // up. Note that a gate will never get more threads waiting on it
+- // after it has been opened, since the process should no longer be
+- // valid and therefore will not have an entry in 'processes'.
+-
+- Gate* gate = NULL;
+- Gate::state_t old;
+-
+- ProcessBase* process = NULL; // Set to non-null if we donate thread.
+-
+- // Try and approach the gate if necessary.
+- synchronized (processes) {
+- if (processes.count(pid.id) > 0) {
+- process = processes[pid.id];
+- CHECK(process->state != ProcessBase::TERMINATED);
+-
+- // Check and see if a gate already exists.
+- if (gates.find(process) == gates.end()) {
+- gates[process] = new Gate();
+- }
+-
+- gate = gates[process];
+- old = gate->approach();
+-
+- // Check if it is runnable in order to donate this thread.
+- if (process->state == ProcessBase::BOTTOM ||
+- process->state == ProcessBase::READY) {
+- synchronized (runq) {
+- list<ProcessBase*>::iterator it =
+- find(runq.begin(), runq.end(), process);
+- if (it != runq.end()) {
+- runq.erase(it);
+- } else {
+- // Another thread has resumed the process ...
+- process = NULL;
+- }
+- }
+- } else {
+- // Process is not runnable, so no need to donate ...
+- process = NULL;
+- }
+- }
+- }
+-
+- if (process != NULL) {
+- VLOG(2) << "Donating thread to " << process->pid << " while waiting";
+- ProcessBase* donator = __process__;
+- __sync_fetch_and_add(&running, 1);
+- process_manager->resume(process);
+- __process__ = donator;
+- }
+-
+- // TODO(benh): Donating only once may not be sufficient, so we might
+- // still deadlock here ... perhaps warn if that's the case?
+-
+- // Now arrive at the gate and wait until it opens.
+- if (gate != NULL) {
+- gate->arrive(old);
+-
+- if (gate->empty()) {
+- delete gate;
+- }
+-
+- return true;
+- }
+-
+- return false;
+-}
+-
+-
+-void ProcessManager::enqueue(ProcessBase* process)
+-{
+- CHECK(process != NULL);
+-
+- // TODO(benh): Check and see if this process has it's own thread. If
+- // it does, push it on that threads runq, and wake up that thread if
+- // it's not running. Otherwise, check and see which thread this
+- // process was last running on, and put it on that threads runq.
+-
+- synchronized (runq) {
+- CHECK(find(runq.begin(), runq.end(), process) == runq.end());
+- runq.push_back(process);
+- }
+-
+- // Wake up the processing thread if necessary.
+- gate->open();
+-}
+-
+-
+-ProcessBase* ProcessManager::dequeue()
+-{
+- // TODO(benh): Remove a process from this thread's runq. If there
+- // are no processes to run, and this is not a dedicated thread, then
+- // steal one from another threads runq.
+-
+- ProcessBase* process = NULL;
+-
+- synchronized (runq) {
+- if (!runq.empty()) {
+- process = runq.front();
+- runq.pop_front();
+- // Increment the running count of processes in order to support
+- // the Clock::settle() operation (this must be done atomically
+- // with removing the process from the runq).
+- __sync_fetch_and_add(&running, 1);
+- }
+- }
+-
+- return process;
+-}
+-
+-
+-void ProcessManager::settle()
+-{
+- bool done = true;
+- do {
+- os::sleep(Milliseconds(10));
+- done = true;
+- // Hopefully this is the only place we acquire both these locks.
+- synchronized (runq) {
+- synchronized (timeouts) {
+- CHECK(Clock::paused()); // Since another thread could resume the clock!
+-
+- if (!runq.empty()) {
+- done = false;
+- }
+-
+- __sync_synchronize(); // Read barrier for 'running'.
+- if (running > 0) {
+- done = false;
+- }
+-
+- if (timeouts->size() > 0 &&
+- timeouts->begin()->first <= clock::current) {
+- done = false;
+- }
+-
+- if (pending_timers) {
+- done = false;
+- }
+- }
+- }
+- } while (!done);
+-}
+-
+-
+-Future<Response> ProcessManager::__processes__(const Request&)
+-{
+- JSON::Array array;
+-
+- synchronized (processes) {
+- foreachvalue (const ProcessBase* process, process_manager->processes) {
+- JSON::Object object;
+- object.values["id"] = process->pid.id;
+-
+- JSON::Array events;
+-
+- struct JSONVisitor : EventVisitor
+- {
+- JSONVisitor(JSON::Array* _events) : events(_events) {}
+-
+- virtual void visit(const MessageEvent& event)
+- {
+- JSON::Object object;
+- object.values["type"] = "MESSAGE";
+-
+- const Message& message = *event.message;
+-
+- object.values["name"] = message.name;
+- object.values["from"] = string(message.from);
+- object.values["to"] = string(message.to);
+- object.values["body"] = message.body;
+-
+- events->values.push_back(object);
+- }
+-
+- virtual void visit(const HttpEvent& event)
+- {
+- JSON::Object object;
+- object.values["type"] = "HTTP";
+-
+- const Request& request = *event.request;
+-
+- object.values["method"] = request.method;
+- object.values["url"] = request.url;
+-
+- events->values.push_back(object);
+- }
+-
+- virtual void visit(const DispatchEvent& event)
+- {
+- JSON::Object object;
+- object.values["type"] = "DISPATCH";
+- events->values.push_back(object);
+- }
+-
+- virtual void visit(const ExitedEvent& event)
+- {
+- JSON::Object object;
+- object.values["type"] = "EXITED";
+- events->values.push_back(object);
+- }
+-
+- virtual void visit(const TerminateEvent& event)
+- {
+- JSON::Object object;
+- object.values["type"] = "TERMINATE";
+- events->values.push_back(object);
+- }
+-
+- JSON::Array* events;
+- } visitor(&events);
+-
+- foreach (Event* event, process->events) {
+- event->visit(&visitor);
+- }
+-
+- object.values["events"] = events;
+- array.values.push_back(object);
+- }
+- }
+-
+- return OK(array);
+-}
+-
+-
+-Timer Timer::create(
+- const Duration& duration,
+- const lambda::function<void(void)>& thunk)
+-{
+- static uint64_t id = 1; // Start at 1 since Timer() instances use id 0.
+-
+- // Assumes Clock::now() does Clock::now(__process__).
+- Timeout timeout = Timeout::in(duration);
+-
+- UPID pid = __process__ != NULL ? __process__->self() : UPID();
+-
+- Timer timer(__sync_fetch_and_add(&id, 1), timeout, pid, thunk);
+-
+- VLOG(3) << "Created a timer for " << timeout.time();
+-
+- // Add the timer.
+- synchronized (timeouts) {
+- if (timeouts->size() == 0 ||
+- timer.timeout().time() < timeouts->begin()->first) {
+- // Need to interrupt the loop to update/set timer repeat.
+- (*timeouts)[timer.timeout().time()].push_back(timer);
+- update_timer = true;
+- ev_async_send(loop, &async_watcher);
+- } else {
+- // Timer repeat is adequate, just add the timeout.
+- CHECK(timeouts->size() >= 1);
+- (*timeouts)[timer.timeout().time()].push_back(timer);
+- }
+- }
+-
+- return timer;
+-}
+-
+-
+-bool Timer::cancel(const Timer& timer)
+-{
+- bool canceled = false;
+- synchronized (timeouts) {
+- // Check if the timeout is still pending, and if so, erase it. In
+- // addition, erase an empty list if we just removed the last
+- // timeout.
+- Time time = timer.timeout().time();
+- if (timeouts->count(time) > 0) {
+- canceled = true;
+- (*timeouts)[time].remove(timer);
+- if ((*timeouts)[time].empty()) {
+- timeouts->erase(time);
+- }
+- }
+- }
+-
+- return canceled;
+-}
+-
+-
+-ProcessBase::ProcessBase(const string& id)
+-{
+- process::initialize();
+-
+- state = ProcessBase::BOTTOM;
+-
+- pthread_mutexattr_t attr;
+- pthread_mutexattr_init(&attr);
+- pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+- pthread_mutex_init(&m, &attr);
+- pthread_mutexattr_destroy(&attr);
+-
+- refs = 0;
+-
+- pid.id = id != "" ? id : ID::generate();
+- pid.ip = __ip__;
+- pid.port = __port__;
+-
+- // If using a manual clock, try and set current time of process
+- // using happens before relationship between creator and createe!
+- if (Clock::paused()) {
+- synchronized (timeouts) {
+- if (Clock::paused()) {
+- clock::currents->erase(this); // In case the address is reused!
+- if (__process__ != NULL) {
+- Clock::order(__process__, this);
+- } else {
+- Clock::update(this, Clock::now());
+- }
+- }
+- }
+- }
+-}
+-
+-
+-ProcessBase::~ProcessBase() {}
+-
+-
+-void ProcessBase::enqueue(Event* event, bool inject)
+-{
+- CHECK(event != NULL);
+-
+- lock();
+- {
+- if (state != TERMINATING && state != TERMINATED) {
+- if (!inject) {
+- events.push_back(event);
+- } else {
+- events.push_front(event);
+- }
+-
+- if (state == BLOCKED) {
+- state = READY;
+- process_manager->enqueue(this);
+- }
+-
+- CHECK(state == BOTTOM ||
+- state == READY ||
+- state == RUNNING);
+- } else {
+- delete event;
+- }
+- }
+- unlock();
+-}
+-
+-
+-void ProcessBase::inject(const UPID& from, const string& name, const char* data, size_t length)
+-{
+- if (!from)
+- return;
+-
+- Message* message = encode(from, pid, name, string(data, length));
+-
+- enqueue(new MessageEvent(message), true);
+-}
+-
+-
+-void ProcessBase::send(const UPID& to, const string& name, const char* data, size_t length)
+-{
+- if (!to) {
+- return;
+- }
+-
+- // Encode and transport outgoing message.
+- transport(encode(pid, to, name, string(data, length)), this);
+-}
+-
+-
+-void ProcessBase::visit(const MessageEvent& event)
+-{
+- if (handlers.message.count(event.message->name) > 0) {
+- handlers.message[event.message->name](
+- event.message->from,
+- event.message->body);
+- } else if (delegates.count(event.message->name) > 0) {
+- VLOG(1) << "Delegating message '" << event.message->name
+- << "' to " << delegates[event.message->name];
+- Message* message = new Message(*event.message);
+- message->to = delegates[event.message->name];
+- transport(message, this);
+- }
+-}
+-
+-
+-void ProcessBase::visit(const DispatchEvent& event)
+-{
+- (*event.f)(this);
+-}
+-
+-
+-void ProcessBase::visit(const HttpEvent& event)
+-{
+- VLOG(1) << "Handling HTTP event for process '" << pid.id << "'"
+- << " with path: '" << event.request->path << "'";
+-
+- CHECK(event.request->path.find('/') == 0); // See ProcessManager::handle.
+-
+- // Split the path by '/'.
+- vector<string> tokens = strings::tokenize(event.request->path, "/");
+- CHECK(tokens.size() >= 1);
+- CHECK(tokens[0] == pid.id);
+-
+- const string& name = tokens.size() > 1 ? tokens[1] : "";
+-
+- if (handlers.http.count(name) > 0) {
+- // Create the promise to link with whatever gets returned, as well
+- // as a future to wait for the response.
+- std::tr1::shared_ptr<Promise<Response> > promise(
+- new Promise<Response>());
+-
+- Future<Response>* future = new Future<Response>(promise->future());
+-
+- // Get the HttpProxy pid for this socket.
+- PID<HttpProxy> proxy = socket_manager->proxy(event.socket);
+-
+- // Let the HttpProxy know about this request (via the future).
+- dispatch(proxy, &HttpProxy::handle, future, *event.request);
+-
+- // Now call the handler and associate the response with the promise.
+- promise->associate(handlers.http[name](*event.request));
+- } else if (assets.count(name) > 0) {
+- OK response;
+- response.type = Response::PATH;
+- response.path = assets[name].path;
+-
+- // Construct the final path by appending remaining tokens.
+- for (int i = 2; i < tokens.size(); i++) {
+- response.path += "/" + tokens[i];
+- }
+-
+- // Try and determine the Content-Type from an extension.
+- Try<string> basename = os::basename(response.path);
+- if (!basename.isError()) {
+- size_t index = basename.get().find_last_of('.');
+- if (index != string::npos) {
+- string extension = basename.get().substr(index);
+- if (assets[name].types.count(extension) > 0) {
+- response.headers["Content-Type"] = assets[name].types[extension];
+- }
+- }
+- }
+-
+- // TODO(benh): Use "text/plain" for assets that don't have an
+- // extension or we don't have a mapping for? It might be better to
+- // just let the browser guess (or do it's own default).
+-
+- // Get the HttpProxy pid for this socket.
+- PID<HttpProxy> proxy = socket_manager->proxy(event.socket);
+-
+- // Enqueue the response with the HttpProxy so that it respects the
+- // order of requests to account for HTTP/1.1 pipelining.
+- dispatch(proxy, &HttpProxy::enqueue, response, *event.request);
+- } else {
+- VLOG(1) << "Returning '404 Not Found' for '" << event.request->path << "'";
+-
+- // Get the HttpProxy pid for this socket.
+- PID<HttpProxy> proxy = socket_manager->proxy(event.socket);
+-
+- // Enqueue the response with the HttpProxy so that it respects the
+- // order of requests to account for HTTP/1.1 pipelining.
+- dispatch(proxy, &HttpProxy::enqueue, NotFound(), *event.request);
+- }
+-}
+-
+-
+-void ProcessBase::visit(const ExitedEvent& event)
+-{
+- exited(event.pid);
+-}
+-
+-
+-void ProcessBase::visit(const TerminateEvent& event)
+-{
+- finalize();
+-}
+-
+-
+-UPID ProcessBase::link(const UPID& to)
+-{
+- if (!to) {
+- return to;
+- }
+-
+- process_manager->link(this, to);
+-
+- return to;
+-}
+-
+-
+-bool ProcessBase::route(
+- const string& name,
+- const Option<string>& help_,
+- const HttpRequestHandler& handler)
+-{
+- if (name.find('/') != 0) {
+- return false;
+- }
+- handlers.http[name.substr(1)] = handler;
+- dispatch(help, &Help::add, pid.id, name, help_);
+- return true;
+-}
+-
+-
+-
+-UPID spawn(ProcessBase* process, bool manage)
+-{
+- process::initialize();
+-
+- if (process != NULL) {
+- // If using a manual clock, try and set current time of process
+- // using happens before relationship between spawner and spawnee!
+- if (Clock::paused()) {
+- synchronized (timeouts) {
+- if (Clock::paused()) {
+- if (__process__ != NULL) {
+- Clock::order(__process__, process);
+- } else {
+- Clock::update(process, Clock::now());
+- }
+- }
+- }
+- }
+-
+- return process_manager->spawn(process, manage);
+- } else {
+- return UPID();
+- }
+-}
+-
+-
+-void terminate(const UPID& pid, bool inject)
+-{
+- process_manager->terminate(pid, inject, __process__);
+-}
+-
+-
+-class WaitWaiter : public Process<WaitWaiter>
+-{
+-public:
+- WaitWaiter(const UPID& _pid, const Duration& _duration, bool* _waited)
+- : ProcessBase(ID::generate("__waiter__")),
+- pid(_pid),
+- duration(_duration),
+- waited(_waited) {}
+-
+- virtual void initialize()
+- {
+- VLOG(3) << "Running waiter process for " << pid;
+- link(pid);
+- delay(duration, self(), &WaitWaiter::timeout);
+- }
+-
+-private:
+- virtual void exited(const UPID&)
+- {
+- VLOG(3) << "Waiter process waited for " << pid;
+- *waited = true;
+- terminate(self());
+- }
+-
+- void timeout()
+- {
+- VLOG(3) << "Waiter process timed out waiting for " << pid;
+- *waited = false;
+- terminate(self());
+- }
+-
+-private:
+- const UPID pid;
+- const Duration duration;
+- bool* const waited;
+-};
+-
+-
+-bool wait(const UPID& pid, const Duration& duration)
+-{
+- process::initialize();
+-
+- if (!pid) {
+- return false;
+- }
+-
+- // This could result in a deadlock if some code decides to wait on a
+- // process that has invoked that code!
+- if (__process__ != NULL && __process__->self() == pid) {
+- std::cerr << "\n**** DEADLOCK DETECTED! ****\nYou are waiting on process "
+- << pid << " that it is currently executing." << std::endl;
+- }
+-
+- if (duration == Seconds(-1)) {
+- return process_manager->wait(pid);
+- }
+-
+- bool waited = false;
+-
+- WaitWaiter waiter(pid, duration, &waited);
+- spawn(waiter);
+- wait(waiter);
+-
+- return waited;
+-}
+-
+-
+-void filter(Filter *filter)
+-{
+- process::initialize();
+-
+- synchronized (filterer) {
+- filterer = filter;
+- }
+-}
+-
+-
+-void post(const UPID& to, const string& name, const char* data, size_t length)
+-{
+- process::initialize();
+-
+- if (!to) {
+- return;
+- }
+-
+- // Encode and transport outgoing message.
+- transport(encode(UPID(), to, name, string(data, length)));
+-}
+-
+-
+-void post(const UPID& from,
+- const UPID& to,
+- const string& name,
+- const char* data,
+- size_t length)
+-{
+- process::initialize();
+-
+- if (!to) {
+- return;
+- }
+-
+- // Encode and transport outgoing message.
+- transport(encode(from, to, name, string(data, length)));
+-}
+-
+-
+-namespace io {
+-
+-namespace internal {
+-
+-void read(int fd,
+- void* data,
+- size_t size,
+- const std::tr1::shared_ptr<Promise<size_t> >& promise,
+- const Future<short>& future)
+-{
+- // Ignore this function if the read operation has been cancelled.
+- if (promise->future().isDiscarded()) {
+- return;
+- }
+-
+- // Since promise->future() will be discarded before future is
+- // discarded, we should never see a discarded future here because of
+- // the check in the beginning of this function.
+- CHECK(!future.isDiscarded());
+-
+- if (future.isFailed()) {
+- promise->fail(future.failure());
+- } else {
+- ssize_t length = ::read(fd, data, size);
+- if (length < 0) {
+- if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
+- // Restart the read operation.
+- poll(fd, process::io::READ).onAny(
+- lambda::bind(&internal::read, fd, data, size, promise, lambda::_1));
+- } else {
+- // Error occurred.
+- promise->fail(strerror(errno));
+- }
+- } else {
+- promise->set(length);
+- }
+- }
+-}
+-
+-} // namespace internal {
+-
+-
+-Future<short> poll(int fd, short events)
+-{
+- process::initialize();
+-
+- // TODO(benh): Check if the file descriptor is non-blocking?
+-
+- Promise<short>* promise = new Promise<short>();
+-
+- // Get a copy of the future to avoid any races with the event loop.
+- Future<short> future = promise->future();
+-
+- ev_io* watcher = new ev_io();
+- watcher->data = promise;
+-
+- ev_io_init(watcher, polled, fd, events);
+-
+- // Enqueue the watcher.
+- synchronized (watchers) {
+- watchers->push(watcher);
+- }
+-
+- // Interrupt the loop.
+- ev_async_send(loop, &async_watcher);
+-
+- return future;
+-}
+-
+-
+-Future<size_t> read(int fd, void* data, size_t size)
+-{
+- process::initialize();
+-
+- std::tr1::shared_ptr<Promise<size_t> > promise(new Promise<size_t>());
+-
+- // Check the file descriptor.
+- Try<bool> nonblock = os::isNonblock(fd);
+- if (nonblock.isError()) {
+- // The file descriptor is not valid (e.g. fd has been closed).
+- promise->fail(string("Failed to check O_NONBLOCK") + strerror(errno));
+- return promise->future();
+- } else if (!nonblock.get()) {
+- // The fd is not opened with O_NONBLOCK set.
+- promise->fail("Please use a fd opened with O_NONBLOCK set");
+- return promise->future();
+- }
+-
+- if (size == 0) {
+- promise->fail("Try to read nothing");
+- return promise->future();
+- }
+-
+- // Because the file descriptor is non-blocking, we call read()
+- // immediately. The read may in turn call poll if needed, avoiding
+- // unnecessary polling. We also observed that for some combination
+- // of libev and Linux kernel versions, the poll would block for
+- // non-deterministically long periods of time. This may be fixed in
+- // a newer version of libev (we use 3.8 at the time of writing this
+- // comment).
+- internal::read(fd, data, size, promise, io::READ);
+-
+- return promise->future();
+-}
+-
+-namespace internal {
+-
+-#if __cplusplus >= 201103L
+-Future<string> _read(int fd,
+- const std::tr1::shared_ptr<string>& buffer,
+- const boost::shared_array<char>& data,
+- size_t length)
+-{
+- return io::read(fd, data.get(), length)
+- .then([=] (size_t size) {
+- if (size == 0) { // EOF.
+- return string(*buffer);
+- }
+- buffer->append(data, size);
+- return _read(fd, buffer, data, length);
+- });
+-}
+-#else
+-// Forward declataion.
+-Future<string> _read(int fd,
+- const std::tr1::shared_ptr<string>& buffer,
+- const boost::shared_array<char>& data,
+- size_t length);
+-
+-
+-Future<string> __read(
+- const size_t& size,
+- // TODO(benh): Remove 'const &' after fixing libprocess.
+- int fd,
+- const std::tr1::shared_ptr<string>& buffer,
+- const boost::shared_array<char>& data,
+- size_t length)
+-{
+- if (size == 0) { // EOF.
+- return string(*buffer);
+- }
+-
+- buffer->append(data.get(), size);
+-
+- return _read(fd, buffer, data, length);
+-}
+-
+-
+-Future<string> _read(int fd,
+- const std::tr1::shared_ptr<string>& buffer,
+- const boost::shared_array<char>& data,
+- size_t length)
+-{
+- return io::read(fd, data.get(), length)
+- .then(lambda::bind(&__read, lambda::_1, fd, buffer, data, length));
+-}
+-#endif
+-
+-} // namespace internal
+-
+-
+-Future<string> read(int fd)
+-{
+- process::initialize();
+-
+- // TODO(benh): Wrap up this data as a struct, use 'Owner'.
+- // TODO(bmahler): For efficiency, use a rope for the buffer.
+- std::tr1::shared_ptr<string> buffer(new string());
+- boost::shared_array<char> data(new char[BUFFERED_READ_SIZE]);
+-
+- return internal::_read(fd, buffer, data, BUFFERED_READ_SIZE);
+-}
+-
+-
+-} // namespace io {
+-
+-
+-namespace http {
+-
+-namespace internal {
+-
+-Future<Response> decode(const string& buffer)
+-{
+- ResponseDecoder decoder;
+- deque<Response*> responses = decoder.decode(buffer.c_str(), buffer.length());
+-
+- if (decoder.failed() || responses.empty()) {
+- for (size_t i = 0; i < responses.size(); ++i) {
+- delete responses[i];
+- }
+- return Failure("Failed to decode HTTP response:\n" + buffer + "\n");
+- } else if (responses.size() > 1) {
+- PLOG(ERROR) << "Received more than 1 HTTP Response";
+- }
+-
+- Response response = *responses[0];
+- for (size_t i = 0; i < responses.size(); ++i) {
+- delete responses[i];
+- }
+-
+- return response;
+-}
+-
+-} // namespace internal {
+-
+-
+-Future<Response> get(const UPID& upid, const string& path, const string& query)
+-{
+- int s = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
+-
+- if (s < 0) {
+- return Failure(string("Failed to create socket: ") + strerror(errno));
+- }
+-
+- Try<Nothing> cloexec = os::cloexec(s);
+- if (!cloexec.isSome()) {
+- return Failure("Failed to cloexec: " + cloexec.error());
+- }
+-
+- sockaddr_in addr;
+- memset(&addr, 0, sizeof(addr));
+- addr.sin_family = AF_INET;
+- addr.sin_port = htons(upid.port);
+- addr.sin_addr.s_addr = upid.ip;
+-
+- if (connect(s, (sockaddr*) &addr, sizeof(addr)) < 0) {
+- os::close(s);
+- return Failure(string("Failed to connect: ") + strerror(errno));
+- }
+-
+- std::ostringstream out;
+-
+- if (query.empty()) {
+- out << "GET /" << upid.id << "/" << path << " HTTP/1.1\r\n";
+- } else {
+- out << "GET /" << upid.id << "/" << path << "?" << query << " HTTP/1.1\r\n";
+- }
+-
+- // Call inet_ntop since inet_ntoa is not thread-safe!
+- char ip[INET_ADDRSTRLEN];
+- PCHECK(inet_ntop(AF_INET, (in_addr *) &upid.ip, ip, INET_ADDRSTRLEN) != NULL);
+-
+- out << "Host: " << ip << ":" << upid.port << "\r\n"
+- << "Connection: close\r\n"
+- << "\r\n";
+-
+- // TODO(bmahler): Use benh's async write when it gets committed.
+- const string& data = out.str();
+- int remaining = data.size();
+-
+- while (remaining > 0) {
+- int n = write(s, data.data() + (data.size() - remaining), remaining);
+-
+- if (n < 0) {
+- if (errno == EINTR) {
+- continue;
+- }
+- os::close(s);
+- return Failure(string("Failed to write: ") + strerror(errno));
+- }
+-
+- remaining -= n;
+- }
+-
+- Try<Nothing> nonblock = os::nonblock(s);
+- if (!nonblock.isSome()) {
+- os::close(s);
+- return Failure("Failed to set nonblock: " + nonblock.error());
+- }
+-
+- // Decode once the async read completes.
+- return io::read(s)
+- .then(lambda::bind(&internal::decode, lambda::_1))
+- .onAny(lambda::bind(&os::close, s));
+-}
+-
+-} // namespace http {
+-
+-namespace internal {
+-
+-void dispatch(
+- const UPID& pid,
+- const std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> >& f,
+- const string& method)
+-{
+- process::initialize();
+-
+- DispatchEvent* event = new DispatchEvent(pid, f, method);
+- process_manager->deliver(pid, event, __process__);
+-}
+-
+-} // namespace internal {
+-} // namespace process {
+diff --git a/3rdparty/libprocess/src/statistics.cpp b/3rdparty/libprocess/src/statistics.cpp
+deleted file mode 100644
+index d4ba9f1..0000000
+--- a/3rdparty/libprocess/src/statistics.cpp
++++ /dev/null
+@@ -1,538 +0,0 @@
+-#include <glog/logging.h>
+-
+-#include <algorithm>
+-#include <list>
+-#include <map>
+-#include <string>
+-#include <utility>
+-#include <vector>
+-
+-#include <process/clock.hpp>
+-#include <process/delay.hpp>
+-#include <process/dispatch.hpp>
+-#include <process/future.hpp>
+-#include <process/help.hpp>
+-#include <process/http.hpp>
+-#include <process/process.hpp>
+-#include <process/statistics.hpp>
+-#include <process/time.hpp>
+-
+-#include <stout/error.hpp>
+-#include <stout/duration.hpp>
+-#include <stout/foreach.hpp>
+-#include <stout/hashmap.hpp>
+-#include <stout/hashset.hpp>
+-#include <stout/json.hpp>
+-#include <stout/none.hpp>
+-#include <stout/numify.hpp>
+-#include <stout/option.hpp>
+-#include <stout/stringify.hpp>
+-#include <stout/strings.hpp>
+-
+-using namespace process;
+-using namespace process::http;
+-
+-using std::list;
+-using std::map;
+-using std::string;
+-using std::vector;
+-
+-namespace process {
+-
+-// This is initialized by process::initialize().
+-Statistics* statistics = NULL;
+-
+-// TODO(bmahler): Move time series related logic into this struct.
+-// TODO(bmahler): Investigate using google's btree implementation.
+-// This provides better insertion and lookup performance for large
+-// containers. This _should_ also provide significant memory
+-// savings, especially since:
+-// 1. Our insertion order will mostly be in sorted order.
+-// 2. Our keys (Seconds) have efficient comparison operators.
+-// See: http://code.google.com/p/cpp-btree/
+-// http://code.google.com/p/cpp-btree/wiki/UsageInstructions
+-struct TimeSeries
+-{
+- TimeSeries() : values(), archived(false) {}
+-
+- // We use a map instead of a hashmap to store the values because
+- // that way we can retrieve a series in sorted order efficiently.
+- map<Time, double> values;
+- bool archived;
+-};
+-
+-
+-class StatisticsProcess : public Process<StatisticsProcess>
+-{
+-public:
+- StatisticsProcess(const Duration& _window)
+- : ProcessBase("statistics"),
+- window(_window) {}
+-
+- virtual ~StatisticsProcess() {}
+-
+- // Statistics implementation.
+- map<Time, double> timeseries(
+- const string& context,
+- const string& name,
+- const Option<Time>& start,
+- const Option<Time>& stop);
+-
+- Option<double> get(const string& context, const string& name);
+-
+- map<string, double> get(const string& context);
+-
+- Try<Nothing> meter(
+- const string& context,
+- const string& name,
+- const Owned<meters::Meter>& meter);
+-
+- void set(
+- const string& context,
+- const string& name,
+- double value,
+- const Time& time);
+-
+- void archive(const string& context, const string& name);
+-
+- void increment(const string& context, const string& name);
+-
+- void decrement(const string& context, const string& name);
+-
+-protected:
+- virtual void initialize()
+- {
+- route("/snapshot.json", SNAPSHOT_HELP, &StatisticsProcess::snapshot);
+- route("/series.json", SERIES_HELP, &StatisticsProcess::series);
+-
+- // Schedule the first truncation.
+- delay(STATISTICS_TRUNCATION_INTERVAL, self(), &StatisticsProcess::truncate);
+- }
+-
+-private:
+- static const string SNAPSHOT_HELP;
+- static const string SERIES_HELP;
+-
+- // Removes values for the specified statistic that occurred outside
+- // the time series window.
+- // NOTE: We always ensure there is at least 1 value left for a statistic,
+- // unless it is archived!
+- // Returns true iff the time series is empty.
+- bool truncate(const string& context, const string& name);
+-
+- // Removes values for all statistics that occurred outside the time
+- // series window.
+- // NOTE: Runs periodically every STATISTICS_TRUNCATION_INTERVAL.
+- // NOTE: We always ensure there is at least 1 value left for a statistic,
+- // unless it is archived.
+- void truncate();
+-
+- // Returns the a snapshot of all statistics in JSON.
+- Future<Response> snapshot(const Request& request);
+-
+- // Returns the time series of a statistic in JSON.
+- Future<Response> series(const Request& request);
+-
+- const Duration window;
+-
+- // This maps from {context: {name: TimeSeries } }.
+- hashmap<string, hashmap<string, TimeSeries> > statistics;
+-
+- // Each statistic can have many meters.
+- // This maps from {context: {name: [meters] } }.
+- hashmap<string, hashmap<string, list<Owned<meters::Meter> > > > meters;
+-};
+-
+-
+-const string StatisticsProcess::SERIES_HELP = HELP(
+- TLDR(
+- "Provides the time series for ..."),
+- USAGE(
+- "/statistics/series.json..."),
+- DESCRIPTION(
+- "...",
+- "",
+- "Query parameters:",
+- "",
+- "> param=VALUE Some description here"));
+-
+-
+-const string StatisticsProcess::SNAPSHOT_HELP = HELP(
+- TLDR(
+- "Provides a snapshot of the current statistics ..."),
+- USAGE(
+- "/statistics/snapshot.json..."),
+- DESCRIPTION(
+- "...",
+- "",
+- "Query parameters:",
+- "",
+- "> param=VALUE Some description here"));
+-
+-
+-Try<Nothing> StatisticsProcess::meter(
+- const string& context,
+- const string& name,
+- const Owned<meters::Meter>& meter)
+-{
+- if (meter->name == name) {
+- return Error("Meter name must not match the statistic name");
+- }
+-
+- // Check for a duplicate meter.
+- foreachkey (const string& context, meters) {
+- foreachkey (const string& name, meters[context]) {
+- foreach (Owned<meters::Meter>& existing, meters[context][name]) {
+- if (meter->name == existing->name) {
+- return Error("Meter name matched existing meter name");
+- }
+- }
+- }
+- }
+-
+- // Add the meter.
+- meters[context][name].push_back(meter);
+-
+- return Nothing();
+-}
+-
+-
+-map<Time, double> StatisticsProcess::timeseries(
+- const string& context,
+- const string& name,
+- const Option<Time>& start,
+- const Option<Time>& stop)
+-{
+- if (!statistics.contains(context) || !statistics[context].contains(name)) {
+- return map<Time, double>();
+- }
+-
+- const std::map<Time, double>& values =
+- statistics[context].find(name)->second.values;
+-
+- map<Time, double>::const_iterator lower = values.lower_bound(start.isSome()
+- ? start.get() : Time::EPOCH);
+-
+- map<Time, double>::const_iterator upper = values.upper_bound(stop.isSome()
+- ? stop.get() : Time::MAX);
+-
+- return map<Time, double>(lower, upper);
+-}
+-
+-
+-Option<double> StatisticsProcess::get(const string& context, const string& name)
+-{
+- if (!statistics.contains(context) ||
+- !statistics[context].contains(name) ||
+- statistics[context][name].values.empty()) {
+- return Option<double>::none();
+- } else {
+- return statistics[context][name].values.rbegin()->second;
+- }
+-}
+-
+-
+-map<string, double> StatisticsProcess::get(const string& context)
+-{
+- map<string, double> results;
+-
+- if (!statistics.contains(context)) {
+- return results;
+- }
+-
+- foreachkey (const string& name, statistics[context]) {
+- const map<Time, double>& values = statistics[context][name].values;
+-
+- if (!values.empty()) {
+- results[name] = values.rbegin()->second;
+- }
+- }
+-
+- return results;
+-}
+-
+-
+-void StatisticsProcess::set(
+- const string& context,
+- const string& name,
+- double value,
+- const Time& time)
+-{
+- statistics[context][name].values[time] = value; // Update the raw value.
+- statistics[context][name].archived = false; // Unarchive.
+-
+- truncate(context, name);
+-
+- // Update the metered values, if necessary.
+- if (meters.contains(context) && meters[context].contains(name)) {
+- foreach (Owned<meters::Meter>& meter, meters[context][name]) {
+- const Option<double>& update = meter->update(time, value);
+- statistics[context][meter->name].archived = false; // Unarchive.
+-
+- if (update.isSome()) {
+- statistics[context][meter->name].values[time] = update.get();
+- truncate(context, meter->name);
+- }
+- }
+- }
+-}
+-
+-
+-void StatisticsProcess::archive(const string& context, const string& name)
+-{
+- // Exclude the statistic from the snapshot.
+- statistics[context][name].archived = true;
+-
+- // Remove any meters as well.
+- if (meters.contains(context) && meters[context].contains(name)) {
+- foreach (const Owned<meters::Meter>& meter, meters[context][name]) {
+- statistics[context][meter->name].archived = true;
+- }
+- meters[context].erase(name);
+- }
+-}
+-
+-
+-void StatisticsProcess::increment(const string& context, const string& name)
+-{
+- double value = 0.0;
+- if (!statistics[context][name].values.empty()) {
+- value = statistics[context][name].values.rbegin()->second;
+- }
+- set(context, name, value + 1.0, Clock::now());
+-}
+-
+-
+-void StatisticsProcess::decrement(const string& context, const string& name)
+-{
+- double value = 0.0;
+- if (!statistics[context][name].values.empty()) {
+- value = statistics[context][name].values.rbegin()->second;
+- }
+- set(context, name, value - 1.0, Clock::now());
+-}
+-
+-
+-bool StatisticsProcess::truncate(const string& context, const string& name)
+-{
+- CHECK(statistics.contains(context));
+- CHECK(statistics[context].contains(name));
+-
+- if (statistics[context][name].values.empty()) {
+- return true; // No truncation is needed, the time series is already empty.
+- }
+-
+- map<Time, double>::iterator start =
+- statistics[context][name].values.begin();
+-
+- while ((Clock::now() - start->first) > window) {
+- // Always keep at least one value for a statistic, unless it's archived!
+- if (statistics[context][name].values.size() == 1) {
+- if (statistics[context][name].archived) {
+- statistics[context][name].values.clear();
+- }
+- break;
+- }
+-
+- statistics[context][name].values.erase(start);
+- start = statistics[context][name].values.begin();
+- }
+-
+- return statistics[context][name].values.empty();
+-}
+-
+-
+-void StatisticsProcess::truncate()
+-{
+- hashmap<string, hashset<string> > empties;
+-
+- foreachkey (const string& context, statistics) {
+- foreachkey (const string& name, statistics[context]) {
+- // Keep track of the emptied timeseries.
+- if (truncate(context, name)) {
+- empties[context].insert(name);
+- }
+- }
+- }
+-
+- // Remove the empty timeseries.
+- foreachkey (const string& context, empties) {
+- foreach (const string& name, empties[context]) {
+- statistics[context].erase(name);
+- }
+- }
+-
+- delay(STATISTICS_TRUNCATION_INTERVAL, self(), &StatisticsProcess::truncate);
+-}
+-
+-
+-Future<Response> StatisticsProcess::snapshot(const Request& request)
+-{
+- JSON::Array array;
+-
+- Option<string> queryContext = request.query.get("context");
+- Option<string> queryName = request.query.get("name");
+-
+- foreachkey (const string& context, statistics) {
+- foreachkey (const string& name, statistics[context]) {
+- // Exclude archived and empty time series.
+- if (statistics[context][name].archived ||
+- statistics[context][name].values.empty()) {
+- continue;
+- }
+-
+- // Skip statistics that don't match the query, if present.
+- if (queryContext.isSome() && queryContext.get() != context) {
+- continue;
+- } else if (queryName.isSome() && queryName.get() != name) {
+- continue;
+- }
+-
+- JSON::Object object;
+- object.values["context"] = context;
+- object.values["name"] = name;
+- object.values["time"] =
+- statistics[context][name].values.rbegin()->first.secs();
+- object.values["value"] =
+- statistics[context][name].values.rbegin()->second;
+- array.values.push_back(object);
+- }
+- }
+-
+- return OK(array, request.query.get("jsonp"));
+-}
+-
+-
+-Future<Response> StatisticsProcess::series(const Request& request)
+-{
+- Option<string> context = request.query.get("context");
+- Option<string> name = request.query.get("name");
+-
+- if (!context.isSome()) {
+- return BadRequest("Expected 'context=val' in query.\n");
+- } else if (!name.isSome()) {
+- return BadRequest("Expected 'name=val' in query.\n");
+- }
+-
+- Option<Time> start = None();
+- Option<Time> stop = None();
+-
+- if (request.query.get("start").isSome()) {
+- Try<double> result = numify<double>(request.query.get("start").get());
+- if (result.isError()) {
+- return BadRequest("Failed to parse 'start': " + result.error());
+- }
+-
+- Try<Time> start_ = Time::create(result.get());
+- if (start_.isError()) {
+- return BadRequest("Failed to parse 'start': " + start_.error());
+- }
+- start = start_.get();
+- }
+-
+- if (request.query.get("stop").isSome()) {
+- Try<double> result = numify<double>(request.query.get("stop").get());
+- if (result.isError()) {
+- return BadRequest("Failed to parse 'stop': " + result.error());
+- }
+-
+- Try<Time> stop_ = Time::create(result.get());
+- if (stop_.isError()) {
+- return BadRequest("Failed to parse 'stop': " + stop_.error());
+- }
+- stop = stop_.get();
+- }
+-
+- JSON::Array array;
+-
+- const map<Time, double>& values =
+- timeseries(context.get(), name.get(), start, stop);
+-
+- foreachpair (const Time& s, double value, values) {
+- JSON::Object object;
+- object.values["time"] = s.secs();
+- object.values["value"] = value;
+- array.values.push_back(object);
+- }
+-
+- return OK(array, request.query.get("jsonp"));
+-}
+-
+-
+-Statistics::Statistics(const Duration& window)
+-{
+- process = new StatisticsProcess(window);
+- spawn(process);
+-}
+-
+-
+-Statistics::~Statistics()
+-{
+- terminate(process);
+- wait(process);
+-}
+-
+-
+-Future<map<Time, double> > Statistics::timeseries(
+- const string& context,
+- const string& name,
+- const Option<Time>& start,
+- const Option<Time>& stop)
+-{
+- return dispatch(
+- process, &StatisticsProcess::timeseries, context, name, start, stop);
+-}
+-
+-
+-Future<Option<double> > Statistics::get(
+- const string& context,
+- const string& name)
+-{
+- return dispatch(process, &StatisticsProcess::get, context, name);
+-}
+-
+-
+-Future<map<string, double> > Statistics::get(const string& context)
+-{
+- return dispatch(process, &StatisticsProcess::get, context);
+-}
+-
+-
+-Future<Try<Nothing> > Statistics::meter(
+- const string& context,
+- const string& name,
+- Owned<meters::Meter> meter)
+-{
+-
+- return dispatch(process, &StatisticsProcess::meter, context, name, meter);
+-}
+-
+-
+-void Statistics::set(
+- const string& context,
+- const string& name,
+- double value,
+- const Time& time)
+-{
+- dispatch(process, &StatisticsProcess::set, context, name, value, time);
+-}
+-
+-
+-void Statistics::archive(const string& context, const string& name)
+-{
+- dispatch(process, &StatisticsProcess::archive, context, name);
+-}
+-
+-
+-void Statistics::increment(const string& context, const string& name)
+-{
+- dispatch(process, &StatisticsProcess::increment, context, name);
+-}
+-
+-
+-void Statistics::decrement(const string& context, const string& name)
+-{
+- dispatch(process, &StatisticsProcess::decrement, context, name);
+-}
+-
+-} // namespace process {
+diff --git a/3rdparty/libprocess/src/synchronized.cpp b/3rdparty/libprocess/src/synchronized.cpp
+deleted file mode 100644
+index 79b0849..0000000
+--- a/3rdparty/libprocess/src/synchronized.cpp
++++ /dev/null
+@@ -1,66 +0,0 @@
+-#include "synchronized.hpp"
+-
+-using std::string;
+-
+-
+-static string s1;
+-static synchronizable(s1);
+-
+-static string s2;
+-static synchronizable(s2) = SYNCHRONIZED_INITIALIZER;
+-
+-static string s3;
+-static synchronizable(s3) = SYNCHRONIZED_INITIALIZER_RECURSIVE;
+-
+-
+-void bar()
+-{
+- synchronized(s3) {
+-
+- }
+-}
+-
+-
+-void foo()
+-{
+- synchronized(s3) {
+- bar();
+- }
+-}
+-
+-
+-class Foo
+-{
+-public:
+- Foo()
+- {
+- synchronizer(s) = SYNCHRONIZED_INITIALIZER_RECURSIVE;
+- }
+-
+- void foo()
+- {
+- synchronized(s) {
+- synchronized(s) {
+-
+- }
+- }
+- }
+-
+-private:
+- string s;
+- synchronizable(s);
+-};
+-
+-
+-int main(int argc, char **argv)
+-{
+- synchronizer(s1) = SYNCHRONIZED_INITIALIZER_RECURSIVE;
+- //synchronizer(s2) = SYNCHRONIZED_INITIALIZER;
+-
+- //foo();
+-
+- Foo f;
+- f.foo();
+-
+- return 0;
+-}
+diff --git a/3rdparty/libprocess/src/synchronized.hpp b/3rdparty/libprocess/src/synchronized.hpp
+deleted file mode 100644
+index 7e0efe2..0000000
+--- a/3rdparty/libprocess/src/synchronized.hpp
++++ /dev/null
+@@ -1,104 +0,0 @@
+-#include <pthread.h>
+-
+-#include <iostream>
+-
+-
+-class Synchronizable
+-{
+-public:
+- Synchronizable()
+- : initialized(false) {}
+-
+- explicit Synchronizable(int _type)
+- : type(_type), initialized(false)
+- {
+- initialize();
+- }
+-
+- Synchronizable(const Synchronizable &that)
+- {
+- type = that.type;
+- initialize();
+- }
+-
+- Synchronizable & operator = (const Synchronizable &that)
+- {
+- type = that.type;
+- initialize();
+- return *this;
+- }
+-
+- void acquire()
+- {
+- if (!initialized) {
+- std::cerr << "synchronizable not initialized" << std::endl;
+- abort();
+- }
+- pthread_mutex_lock(&mutex);
+- }
+-
+- void release()
+- {
+- if (!initialized) {
+- std::cerr << "synchronizable not initialized" << std::endl;
+- abort();
+- }
+- pthread_mutex_unlock(&mutex);
+- }
+-
+-private:
+- void initialize()
+- {
+- if (!initialized) {
+- pthread_mutexattr_t attr;
+- pthread_mutexattr_init(&attr);
+- pthread_mutexattr_settype(&attr, type);
+- pthread_mutex_init(&mutex, &attr);
+- pthread_mutexattr_destroy(&attr);
+- initialized = true;
+- } else {
+- std::cerr << "synchronizable already initialized" << std::endl;
+- abort();
+- }
+- }
+-
+- int type;
+- bool initialized;
+- pthread_mutex_t mutex;
+-};
+-
+-
+-class Synchronized
+-{
+-public:
+- Synchronized(Synchronizable *_synchronizable)
+- : synchronizable(_synchronizable)
+- {
+- synchronizable->acquire();
+- }
+-
+- ~Synchronized()
+- {
+- synchronizable->release();
+- }
+-
+- operator bool () { return true; }
+-
+-private:
+- Synchronizable *synchronizable;
+-};
+-
+-
+-#define synchronized(s) \
+- if (Synchronized __synchronized ## s = Synchronized(&__synchronizable_ ## s))
+-
+-#define synchronizable(s) \
+- Synchronizable __synchronizable_ ## s
+-
+-#define synchronizer(s) \
+- (__synchronizable_ ## s)
+-
+-
+-#define SYNCHRONIZED_INITIALIZER Synchronizable(PTHREAD_MUTEX_NORMAL)
+-#define SYNCHRONIZED_INITIALIZER_DEBUG Synchronizable(PTHREAD_MUTEX_ERRORCHECK)
+-#define SYNCHRONIZED_INITIALIZER_RECURSIVE Synchronizable(PTHREAD_MUTEX_RECURSIVE)
+diff --git a/3rdparty/libprocess/src/test-master.cpp b/3rdparty/libprocess/src/test-master.cpp
+deleted file mode 100644
+index f272919..0000000
+--- a/3rdparty/libprocess/src/test-master.cpp
++++ /dev/null
+@@ -1,62 +0,0 @@
+-#include <io.hpp>
+-#include <tuple.hpp>
+-
+-#include <string>
+-
+-#include "test.hpp"
+-
+-using std::string;
+-
+-
+-using namespace process::tuple;
+-
+-
+-class Master : public Tuple<Process>
+-{
+-private:
+- int id;
+-
+-protected:
+- void operator () ()
+- {
+- do {
+- switch (receive()) {
+- case REGISTER: {
+- Out::println("Master received REGISTER");
+-
+- string name;
+- unpack<REGISTER>(name);
+-
+- Out::println("Registered slave: %s", name.c_str());
+-
+- send(from(), pack<OKAY>(id++));
+- break;
+- }
+- case UNREGISTER: {
+- Out::println("Master received UNREGISTER");
+-
+- int slave_id;
+- unpack<UNREGISTER>(slave_id);
+-
+- Out::println("Unregistered slave id: %d", slave_id);
+-
+- send(from(), pack<OKAY>(0));
+- break;
+- }
+- default:
+- Out::println("UNKNOWN MESSAGE RECEIVED");
+- }
+- } while (true);
+- }
+-
+-public:
+- Master() : id(0) {}
+-};
+-
+-
+-int main(int argc, char **argv)
+-{
+- PID master = Process::spawn(new Master());
+- Out::println("master: %s", string(master).c_str());
+- Process::wait(master);
+-}
+diff --git a/3rdparty/libprocess/src/test-slave.cpp b/3rdparty/libprocess/src/test-slave.cpp
+deleted file mode 100644
+index fe08ce8..0000000
+--- a/3rdparty/libprocess/src/test-slave.cpp
++++ /dev/null
+@@ -1,61 +0,0 @@
+-#include <test.hpp>
+-
+-using namespace process::record;
+-
+-class Slave : public RecordProcess
+-{
+-private:
+- PID master;
+- int id;
+-
+-protected:
+- void operator () ()
+- {
+- send(master, pack<REGISTER>("c3po"));
+-
+- switch (receive()) {
+- case OKAY: {
+- std::cout << "slave registered" << std::endl;
+- unpack<OKAY>(id);
+- std::cout << "slave id: " << id << std::endl;
+- break;
+- }
+- default:
+- std::cout << "slave failed to register" << std::endl;
+- break;
+- }
+-
+- send(master, pack<UNREGISTER>(id));
+-
+- switch (receive()) {
+- case OKAY:
+- std::cout << "slave unregistered" << std::endl;
+- break;
+- default:
+- std::cout << "slave failed to unregister" << std::endl;
+- break;
+- }
+-
+- link(master);
+- switch (receive()) {
+- case PROCESS_EXIT:
+- std::cout << "master exited" << std::endl;
+- break;
+- default:
+- std::cout << "unexpected message" << std::endl;
+- break;
+- }
+- }
+-
+-public:
+- Slave(const PID &_master) : master(_master) {}
+-};
+-
+-
+-int main(int argc, char **argv)
+-{
+- PID master = make_pid(argv[1]);
+- PID slave = Process::spawn(new Slave(master));
+- std::cout << "slave is at " << slave << std::endl;
+- Process::wait(slave);
+-}
+diff --git a/3rdparty/libprocess/src/tests/decoder_tests.cpp b/3rdparty/libprocess/src/tests/decoder_tests.cpp
+deleted file mode 100644
+index 04ca3ff..0000000
+--- a/3rdparty/libprocess/src/tests/decoder_tests.cpp
++++ /dev/null
+@@ -1,128 +0,0 @@
+-#include <gmock/gmock.h>
+-
+-#include <deque>
+-#include <string>
+-
+-#include <process/socket.hpp>
+-
+-#include <stout/gtest.hpp>
+-
+-#include "decoder.hpp"
+-
+-using namespace process;
+-using namespace process::http;
+-
+-using std::deque;
+-using std::string;
+-
+-
+-TEST(Decoder, Request)
+-{
+- DataDecoder decoder = DataDecoder(Socket());
+-
+- const string& data =
+- "GET /path/file.json?key1=value1&key2=value2#fragment HTTP/1.1\r\n"
+- "Host: localhost\r\n"
+- "Connection: close\r\n"
+- "Accept-Encoding: compress, gzip\r\n"
+- "\r\n";
+-
+- deque<Request*> requests = decoder.decode(data.data(), data.length());
+- ASSERT_FALSE(decoder.failed());
+- ASSERT_EQ(1, requests.size());
+-
+- Request* request = requests[0];
+- EXPECT_EQ("GET", request->method);
+- EXPECT_EQ("/path/file.json", request->path);
+- EXPECT_EQ("/path/file.json?key1=value1&key2=value2#fragment", request->url);
+- EXPECT_EQ("fragment", request->fragment);
+- EXPECT_TRUE(request->body.empty());
+- EXPECT_FALSE(request->keepAlive);
+-
+- EXPECT_EQ(3, request->headers.size());
+- EXPECT_SOME_EQ("localhost", request->headers.get("Host"));
+- EXPECT_SOME_EQ("close", request->headers.get("Connection"));
+- EXPECT_SOME_EQ("compress, gzip", request->headers.get("Accept-Encoding"));
+-
+- EXPECT_EQ(2, request->query.size());
+- EXPECT_SOME_EQ("value1", request->query.get("key1"));
+- EXPECT_SOME_EQ("value2", request->query.get("key2"));
+-
+- delete request;
+-}
+-
+-
+-TEST(Decoder, RequestHeaderContinuation)
+-{
+- DataDecoder decoder = DataDecoder(Socket());
+-
+- const string& data =
+- "GET /path/file.json HTTP/1.1\r\n"
+- "Host: localhost\r\n"
+- "Connection: close\r\n"
+- "Accept-Encoding: compress,"
+- " gzip\r\n"
+- "\r\n";
+-
+- deque<Request*> requests = decoder.decode(data.data(), data.length());
+- ASSERT_FALSE(decoder.failed());
+- ASSERT_EQ(1, requests.size());
+-
+- Request* request = requests[0];
+- EXPECT_SOME_EQ("compress, gzip",
+- request->headers.get("Accept-Encoding"));
+- delete request;
+-}
+-
+-
+-// This is expected to fail for now, see my TODO(bmahler) on http::Request.
+-TEST(Decoder, DISABLED_RequestHeaderCaseInsensitive)
+-{
+- DataDecoder decoder = DataDecoder(Socket());
+-
+- const string& data =
+- "GET /path/file.json HTTP/1.1\r\n"
+- "Host: localhost\r\n"
+- "cOnnECtioN: close\r\n"
+- "accept-ENCODING: compress, gzip\r\n"
+- "\r\n";
+-
+- deque<Request*> requests = decoder.decode(data.data(), data.length());
+- ASSERT_FALSE(decoder.failed());
+- ASSERT_EQ(1, requests.size());
+-
+- Request* request = requests[0];
+- EXPECT_FALSE(request->keepAlive);
+-
+- EXPECT_SOME_EQ("compress, gzip", request->headers.get("Accept-Encoding"));
+-
+- delete request;
+-}
+-
+-
+-TEST(Decoder, Response)
+-{
+- ResponseDecoder decoder;
+-
+- const string& data =
+- "HTTP/1.1 200 OK\r\n"
+- "Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n"
+- "Content-Type: text/plain\r\n"
+- "Content-Length: 2\r\n"
+- "\r\n"
+- "hi";
+-
+- deque<Response*> requests = decoder.decode(data.data(), data.length());
+- ASSERT_FALSE(decoder.failed());
+- ASSERT_EQ(1, requests.size());
+-
+- Response* response = requests[0];
+-
+- EXPECT_EQ("200 OK", response->status);
+- EXPECT_EQ(Response::BODY, response->type);
+- EXPECT_EQ("hi", response->body);
+-
+- EXPECT_EQ(3, response->headers.size());
+-
+- delete response;
+-}
+diff --git a/3rdparty/libprocess/src/tests/encoder_tests.cpp b/3rdparty/libprocess/src/tests/encoder_tests.cpp
+deleted file mode 100644
+index fccb865..0000000
+--- a/3rdparty/libprocess/src/tests/encoder_tests.cpp
++++ /dev/null
+@@ -1,92 +0,0 @@
+-#include <gmock/gmock.h>
+-
+-#include <deque>
+-#include <string>
+-#include <vector>
+-
+-#include <process/http.hpp>
+-#include <process/socket.hpp>
+-
+-#include <stout/gtest.hpp>
+-
+-#include "encoder.hpp"
+-#include "decoder.hpp"
+-
+-using namespace process;
+-using namespace process::http;
+-
+-using std::deque;
+-using std::string;
+-using std::vector;
+-
+-
+-TEST(Encoder, Response)
+-{
+- Request request;
+- const OK& response("body");
+-
+- // Encode the response.
+- const string& encoded = HttpResponseEncoder::encode(response, request);
+-
+- // Now decode it back, and verify the encoding was correct.
+- ResponseDecoder decoder;
+- deque<Response*> responses = decoder.decode(encoded.data(), encoded.length());
+- ASSERT_FALSE(decoder.failed());
+- ASSERT_EQ(1, responses.size());
+-
+- Response* decoded = responses[0];
+- EXPECT_EQ("200 OK", decoded->status);
+- EXPECT_EQ("body", decoded->body);
+-
+- // Encoding should have inserted the 'Date' and 'Content-Length' headers.
+- EXPECT_EQ(2, decoded->headers.size());
+- EXPECT_TRUE(decoded->headers.contains("Date"));
+- EXPECT_SOME_EQ(
+- stringify(response.body.size()),
+- decoded->headers.get("Content-Length"));
+-}
+-
+-
+-TEST(Encoder, AcceptableEncodings)
+-{
+- // Create requests that do not accept gzip encoding.
+- vector<Request> requests(7);
+- requests[0].headers["Accept-Encoding"] = "gzip;q=0.0,*";
+- requests[1].headers["Accept-Encoding"] = "compress";
+- requests[2].headers["Accept-Encoding"] = "compress, gzip;q=0.0";
+- requests[3].headers["Accept-Encoding"] = "*, gzip;q=0.0";
+- requests[4].headers["Accept-Encoding"] = "*;q=0.0, compress";
+- requests[5].headers["Accept-Encoding"] = "\n compress";
+- requests[6].headers["Accept-Encoding"] = "compress,\tgzip;q=0.0";
+-
+- foreach (const Request& request, requests) {
+- EXPECT_FALSE(request.accepts("gzip"))
+- << "Gzip encoding is unacceptable for 'Accept-Encoding: "
+- << request.headers.get("Accept-Encoding").get() << "'";
+- }
+-
+- // Create requests that accept gzip encoding.
+- vector<Request> gzipRequests(12);
+-
+- // Using q values.
+- gzipRequests[0].headers["Accept-Encoding"] = "gzip;q=0.1,*";
+- gzipRequests[1].headers["Accept-Encoding"] = "compress, gzip;q=0.1";
+- gzipRequests[2].headers["Accept-Encoding"] = "*, gzip;q=0.5";
+- gzipRequests[3].headers["Accept-Encoding"] = "*;q=0.9, compress";
+- gzipRequests[4].headers["Accept-Encoding"] = "compress,\tgzip;q=0.1";
+-
+- // No q values.
+- gzipRequests[5].headers["Accept-Encoding"] = "gzip";
+- gzipRequests[6].headers["Accept-Encoding"] = "compress, gzip";
+- gzipRequests[7].headers["Accept-Encoding"] = "*";
+- gzipRequests[8].headers["Accept-Encoding"] = "*, compress";
+- gzipRequests[9].headers["Accept-Encoding"] = "\n gzip";
+- gzipRequests[10].headers["Accept-Encoding"] = "compress,\tgzip";
+- gzipRequests[11].headers["Accept-Encoding"] = "gzip";
+-
+- foreach (const Request& gzipRequest, gzipRequests) {
+- EXPECT_TRUE(gzipRequest.accepts("gzip"))
+- << "Gzip encoding is acceptable for 'Accept-Encoding: "
+- << gzipRequest.headers.get("Accept-Encoding").get() << "'";
+- }
+-}
+diff --git a/3rdparty/libprocess/src/tests/http_tests.cpp b/3rdparty/libprocess/src/tests/http_tests.cpp
+deleted file mode 100644
+index 68d1a1b..0000000
+--- a/3rdparty/libprocess/src/tests/http_tests.cpp
++++ /dev/null
+@@ -1,180 +0,0 @@
+-#include <arpa/inet.h>
+-
+-#include <gmock/gmock.h>
+-
+-#include <netinet/in.h>
+-#include <netinet/tcp.h>
+-
+-#include <string>
+-
+-#include <process/future.hpp>
+-#include <process/gmock.hpp>
+-#include <process/gtest.hpp>
+-#include <process/http.hpp>
+-#include <process/io.hpp>
+-
+-#include <stout/gtest.hpp>
+-#include <stout/none.hpp>
+-#include <stout/nothing.hpp>
+-#include <stout/os.hpp>
+-
+-#include "encoder.hpp"
+-
+-using namespace process;
+-
+-using testing::_;
+-using testing::Assign;
+-using testing::DoAll;
+-using testing::Return;
+-
+-
+-class HttpProcess : public Process<HttpProcess>
+-{
+-public:
+- HttpProcess()
+- {
+- route("/body", None(), &HttpProcess::body);
+- route("/pipe", None(), &HttpProcess::pipe);
+- }
+-
+- MOCK_METHOD1(body, Future<http::Response>(const http::Request&));
+- MOCK_METHOD1(pipe, Future<http::Response>(const http::Request&));
+-};
+-
+-
+-TEST(HTTP, Endpoints)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- HttpProcess process;
+-
+- spawn(process);
+-
+- // First hit '/body' (using explicit sockets and HTTP/1.0).
+- int s = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
+-
+- ASSERT_LE(0, s);
+-
+- sockaddr_in addr;
+- memset(&addr, 0, sizeof(addr));
+- addr.sin_family = PF_INET;
+- addr.sin_port = htons(process.self().port);
+- addr.sin_addr.s_addr = process.self().ip;
+-
+- ASSERT_EQ(0, connect(s, (sockaddr*) &addr, sizeof(addr)));
+-
+- std::ostringstream out;
+- out << "GET /" << process.self().id << "/body"
+- << " HTTP/1.0\r\n"
+- << "Connection: Keep-Alive\r\n"
+- << "\r\n";
+-
+- const std::string& data = out.str();
+-
+- EXPECT_CALL(process, body(_))
+- .WillOnce(Return(http::OK()));
+-
+- ASSERT_SOME(os::write(s, data));
+-
+- std::string response = "HTTP/1.1 200 OK";
+-
+- char temp[response.size()];
+- ASSERT_LT(0, ::read(s, temp, response.size()));
+- ASSERT_EQ(response, std::string(temp, response.size()));
+-
+- ASSERT_EQ(0, close(s));
+-
+- // Now hit '/pipe' (by using http::get).
+- int pipes[2];
+- ASSERT_NE(-1, ::pipe(pipes));
+-
+- http::OK ok;
+- ok.type = http::Response::PIPE;
+- ok.pipe = pipes[0];
+-
+- Future<Nothing> pipe;
+- EXPECT_CALL(process, pipe(_))
+- .WillOnce(DoAll(FutureSatisfy(&pipe),
+- Return(ok)));
+-
+- Future<http::Response> future = http::get(process.self(), "pipe");
+-
+- AWAIT_READY(pipe);
+-
+- ASSERT_SOME(os::write(pipes[1], "Hello World\n"));
+- ASSERT_SOME(os::close(pipes[1]));
+-
+- AWAIT_READY(future);
+- ASSERT_EQ(http::statuses[200], future.get().status);
+- ASSERT_EQ("chunked", future.get().headers["Transfer-Encoding"]);
+- ASSERT_EQ("Hello World\n", future.get().body);
+-
+- terminate(process);
+- wait(process);
+-}
+-
+-
+-TEST(HTTP, Encode)
+-{
+- std::string unencoded = "a$&+,/:;=?@ \"<>#%{}|\\^~[]`\x19\x80\xFF";
+- unencoded += std::string("\x00", 1); // Add a null byte to the end.
+-
+- std::string encoded = http::encode(unencoded);
+-
+- EXPECT_EQ("a%24%26%2B%2C%2F%3A%3B%3D%3F%40%20%22%3C%3E%23"
+- "%25%7B%7D%7C%5C%5E%7E%5B%5D%60%19%80%FF%00",
+- encoded);
+-
+- EXPECT_SOME_EQ(unencoded, http::decode(encoded));
+-
+- EXPECT_ERROR(http::decode("%"));
+- EXPECT_ERROR(http::decode("%1"));
+- EXPECT_ERROR(http::decode("%;1"));
+- EXPECT_ERROR(http::decode("%1;"));
+-}
+-
+-
+-TEST(HTTP, PathParse)
+-{
+- const std::string pattern = "/books/{isbn}/chapters/{chapter}";
+-
+- Try<hashmap<std::string, std::string> > parse =
+- http::path::parse(pattern, "/books/0304827484/chapters/3");
+-
+- ASSERT_SOME(parse);
+- EXPECT_EQ(4, parse.get().size());
+- EXPECT_EQ("books", parse.get()["books"]);
+- EXPECT_EQ("0304827484", parse.get()["isbn"]);
+- EXPECT_EQ("chapters", parse.get()["chapters"]);
+- EXPECT_EQ("3", parse.get()["chapter"]);
+-
+- parse = http::path::parse(pattern, "/books/0304827484");
+-
+- ASSERT_SOME(parse);
+- EXPECT_EQ(2, parse.get().size());
+- EXPECT_EQ("books", parse.get()["books"]);
+- EXPECT_EQ("0304827484", parse.get()["isbn"]);
+-
+- parse = http::path::parse(pattern, "/books/0304827484/chapters");
+-
+- ASSERT_SOME(parse);
+- EXPECT_EQ(3, parse.get().size());
+- EXPECT_EQ("books", parse.get()["books"]);
+- EXPECT_EQ("0304827484", parse.get()["isbn"]);
+- EXPECT_EQ("chapters", parse.get()["chapters"]);
+-
+- parse = http::path::parse(pattern, "/foo/0304827484/chapters");
+-
+- EXPECT_ERROR(parse);
+- EXPECT_EQ("Expecting 'books' not 'foo'", parse.error());
+-
+- parse = http::path::parse(pattern, "/books/0304827484/bar");
+-
+- EXPECT_ERROR(parse);
+- EXPECT_EQ("Expecting 'chapters' not 'bar'", parse.error());
+-
+- parse = http::path::parse(pattern, "/books/0304827484/chapters/3/foo/bar");
+-
+- EXPECT_ERROR(parse);
+- EXPECT_EQ("Not expecting suffix 'foo/bar'", parse.error());
+-}
+diff --git a/3rdparty/libprocess/src/tests/io_tests.cpp b/3rdparty/libprocess/src/tests/io_tests.cpp
+deleted file mode 100644
+index ee5b0b4..0000000
+--- a/3rdparty/libprocess/src/tests/io_tests.cpp
++++ /dev/null
+@@ -1,164 +0,0 @@
+-#include <gmock/gmock.h>
+-
+-#include <string>
+-
+-#include <process/future.hpp>
+-#include <process/gtest.hpp>
+-#include <process/io.hpp>
+-
+-#include <stout/gtest.hpp>
+-#include <stout/os.hpp>
+-
+-#include "encoder.hpp"
+-
+-using namespace process;
+-
+-using std::string;
+-
+-
+-TEST(IO, Poll)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- int pipes[2];
+- ASSERT_NE(-1, pipe(pipes));
+-
+- Future<short> future = io::poll(pipes[0], io::READ);
+-
+- EXPECT_FALSE(future.isReady());
+-
+- ASSERT_EQ(3, write(pipes[1], "hi", 3));
+-
+- AWAIT_EXPECT_EQ(io::READ, future);
+-
+- close(pipes[0]);
+- close(pipes[1]);
+-}
+-
+-
+-TEST(IO, Read)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- int pipes[2];
+- char data[3];
+-
+- // Create a blocking pipe.
+- ASSERT_NE(-1, ::pipe(pipes));
+-
+- // Test on a blocking file descriptor.
+- AWAIT_EXPECT_FAILED(io::read(pipes[0], data, 3));
+-
+- close(pipes[0]);
+- close(pipes[1]);
+-
+- // Test on a closed file descriptor.
+- AWAIT_EXPECT_FAILED(io::read(pipes[0], data, 3));
+-
+- // Create a nonblocking pipe.
+- ASSERT_NE(-1, ::pipe(pipes));
+- ASSERT_SOME(os::nonblock(pipes[0]));
+- ASSERT_SOME(os::nonblock(pipes[1]));
+-
+- // Test reading nothing.
+- AWAIT_EXPECT_FAILED(io::read(pipes[0], data, 0));
+-
+- // Test successful read.
+- Future<size_t> future = io::read(pipes[0], data, 3);
+- ASSERT_FALSE(future.isReady());
+-
+- ASSERT_EQ(2, write(pipes[1], "hi", 2));
+-
+- AWAIT_ASSERT_EQ(2u, future);
+- EXPECT_EQ('h', data[0]);
+- EXPECT_EQ('i', data[1]);
+-
+- // Test cancellation.
+- future = io::read(pipes[0], data, 1);
+- ASSERT_FALSE(future.isReady());
+-
+- future.discard();
+-
+- ASSERT_EQ(3, write(pipes[1], "omg", 3));
+-
+- AWAIT_ASSERT_EQ(3u, io::read(pipes[0], data, 3));
+- EXPECT_EQ('o', data[0]);
+- EXPECT_EQ('m', data[1]);
+- EXPECT_EQ('g', data[2]);
+-
+- // Test read EOF.
+- future = io::read(pipes[0], data, 3);
+- ASSERT_FALSE(future.isReady());
+-
+- close(pipes[1]);
+-
+- AWAIT_ASSERT_EQ(0u, future);
+-
+- close(pipes[0]);
+-}
+-
+-
+-TEST(IO, BufferedRead)
+-{
+- // 128 Bytes.
+- string data =
+- "This data is much larger than BUFFERED_READ_SIZE, which means it will "
+- "trigger multiple buffered async reads as a result.........";
+- ASSERT_EQ(128u, data.size());
+-
+- // Keep doubling the data size until we're guaranteed to trigger at least
+- // 3 buffered async reads.
+- while (data.length() < 3 * io::BUFFERED_READ_SIZE) {
+- data.append(data);
+- }
+-
+- // First read from a file.
+- ASSERT_SOME(os::write("file", data));
+-
+- Try<int> fd = os::open("file", O_RDONLY);
+- ASSERT_SOME(fd);
+-
+- // Read from blocking fd.
+- AWAIT_EXPECT_FAILED(io::read(fd.get()));
+-
+- // Read from non-blocking fd.
+- ASSERT_TRUE(os::nonblock(fd.get()).isSome());
+- AWAIT_EXPECT_EQ(data, io::read(fd.get()));
+-
+- os::close(fd.get());
+-
+- // Now read from pipes.
+- int pipes[2];
+-
+- // Create a blocking pipe.
+- ASSERT_NE(-1, ::pipe(pipes));
+-
+- // Test on a blocking pipe.
+- AWAIT_EXPECT_FAILED(io::read(pipes[0]));
+-
+- close(pipes[0]);
+- close(pipes[1]);
+-
+- // Test on a closed pipe.
+- AWAIT_EXPECT_FAILED(io::read(pipes[0]));
+-
+- // Create a nonblocking pipe for reading.
+- ASSERT_NE(-1, ::pipe(pipes));
+- ASSERT_SOME(os::nonblock(pipes[0]));
+-
+- // Test a successful read from the pipe.
+- Future<string> future = io::read(pipes[0]);
+-
+- // At first, the future will not be ready until we write to and
+- // close the pipe.
+- ASSERT_FALSE(future.isReady());
+-
+- ASSERT_SOME(os::write(pipes[1], data));
+- close(pipes[1]);
+-
+- AWAIT_EXPECT_EQ(data, future);
+-
+- close(pipes[0]);
+-
+- ASSERT_SOME(os::rm("file"));
+-}
+diff --git a/3rdparty/libprocess/src/tests/main.cpp b/3rdparty/libprocess/src/tests/main.cpp
+deleted file mode 100644
+index 6c672b4..0000000
+--- a/3rdparty/libprocess/src/tests/main.cpp
++++ /dev/null
+@@ -1,25 +0,0 @@
+-#include <gtest/gtest.h>
+-
+-#include <gmock/gmock.h>
+-
+-#include <process/gmock.hpp>
+-#include <process/gtest.hpp>
+-#include <process/process.hpp>
+-
+-int main(int argc, char** argv)
+-{
+- // Initialize Google Mock/Test.
+- testing::InitGoogleMock(&argc, argv);
+-
+- // Initialize libprocess.
+- process::initialize();
+-
+- // Add the libprocess test event listeners.
+- ::testing::TestEventListeners& listeners =
+- ::testing::UnitTest::GetInstance()->listeners();
+-
+- listeners.Append(process::ClockTestEventListener::instance());
+- listeners.Append(process::FilterTestEventListener::instance());
+-
+- return RUN_ALL_TESTS();
+-}
+diff --git a/3rdparty/libprocess/src/tests/owned_tests.cpp b/3rdparty/libprocess/src/tests/owned_tests.cpp
+deleted file mode 100644
+index 234469b..0000000
+--- a/3rdparty/libprocess/src/tests/owned_tests.cpp
++++ /dev/null
+@@ -1,80 +0,0 @@
+-#include <gmock/gmock.h>
+-
+-#include <process/gtest.hpp>
+-#include <process/owned.hpp>
+-#include <process/shared.hpp>
+-
+-using namespace process;
+-
+-class Foo
+-{
+-public:
+- int get() const { return value; }
+- void set(int _value) { value = _value; }
+-
+-private:
+- int value;
+-};
+-
+-
+-TEST(Owned, Access)
+-{
+- Foo* foo = new Foo();
+- foo->set(42);
+-
+- Owned<Foo> owned(foo);
+-
+- EXPECT_EQ(42, owned->get());
+- EXPECT_EQ(42, (*owned).get());
+- EXPECT_EQ(42, owned.get()->get());
+-
+- owned->set(10);
+-
+- EXPECT_EQ(10, owned->get());
+- EXPECT_EQ(10, (*owned).get());
+- EXPECT_EQ(10, owned.get()->get());
+-}
+-
+-
+-TEST(Owned, Null)
+-{
+- Owned<Foo> owned;
+- Owned<Foo> owned2(NULL);
+-
+- EXPECT_TRUE(owned.get() == NULL);
+- EXPECT_TRUE(owned2.get() == NULL);
+-}
+-
+-
+-TEST(Owned, Share)
+-{
+- Foo* foo = new Foo();
+- foo->set(42);
+-
+- Owned<Foo> owned(foo);
+-
+- EXPECT_EQ(42, owned->get());
+- EXPECT_EQ(42, (*owned).get());
+- EXPECT_EQ(42, owned.get()->get());
+-
+- Shared<Foo> shared = owned.share();
+-
+- EXPECT_TRUE(owned.get() == NULL);
+- EXPECT_TRUE(shared.unique());
+-
+- EXPECT_EQ(42, shared->get());
+- EXPECT_EQ(42, (*shared).get());
+- EXPECT_EQ(42, shared.get()->get());
+-
+- {
+- Shared<Foo> shared2(shared);
+-
+- EXPECT_EQ(42, shared2->get());
+- EXPECT_EQ(42, (*shared2).get());
+- EXPECT_EQ(42, shared2.get()->get());
+- EXPECT_FALSE(shared.unique());
+- EXPECT_FALSE(shared2.unique());
+- }
+-
+- EXPECT_TRUE(shared.unique());
+-}
+diff --git a/3rdparty/libprocess/src/tests/process_tests.cpp b/3rdparty/libprocess/src/tests/process_tests.cpp
+deleted file mode 100644
+index b0fb5c2..0000000
+--- a/3rdparty/libprocess/src/tests/process_tests.cpp
++++ /dev/null
+@@ -1,1259 +0,0 @@
+-#include <arpa/inet.h>
+-
+-#include <gmock/gmock.h>
+-
+-#include <netinet/in.h>
+-#include <netinet/tcp.h>
+-
+-#include <string>
+-#include <sstream>
+-
+-#include <process/async.hpp>
+-#include <process/collect.hpp>
+-#include <process/clock.hpp>
+-#include <process/defer.hpp>
+-#include <process/delay.hpp>
+-#include <process/dispatch.hpp>
+-#include <process/executor.hpp>
+-#include <process/filter.hpp>
+-#include <process/future.hpp>
+-#include <process/gc.hpp>
+-#include <process/gmock.hpp>
+-#include <process/gtest.hpp>
+-#include <process/limiter.hpp>
+-#include <process/process.hpp>
+-#include <process/run.hpp>
+-#include <process/time.hpp>
+-
+-#include <stout/duration.hpp>
+-#include <stout/gtest.hpp>
+-#include <stout/nothing.hpp>
+-#include <stout/os.hpp>
+-#include <stout/stringify.hpp>
+-#include <stout/stopwatch.hpp>
+-
+-#include "encoder.hpp"
+-
+-using namespace process;
+-
+-using std::string;
+-
+-using testing::_;
+-using testing::Assign;
+-using testing::DoAll;
+-using testing::Return;
+-using testing::ReturnArg;
+-
+-// TODO(bmahler): Move tests into their own files as appropriate.
+-
+-TEST(Process, event)
+-{
+- Event* event = new TerminateEvent(UPID());
+- EXPECT_FALSE(event->is<MessageEvent>());
+- EXPECT_FALSE(event->is<ExitedEvent>());
+- EXPECT_TRUE(event->is<TerminateEvent>());
+- delete event;
+-}
+-
+-
+-TEST(Process, future)
+-{
+- Promise<bool> promise;
+- promise.set(true);
+- ASSERT_TRUE(promise.future().isReady());
+- EXPECT_TRUE(promise.future().get());
+-}
+-
+-
+-TEST(Process, associate)
+-{
+- Promise<bool> promise1;
+- Future<bool> future1(true);
+- promise1.associate(future1);
+- ASSERT_TRUE(promise1.future().isReady());
+- EXPECT_TRUE(promise1.future().get());
+-
+- Promise<bool> promise2;
+- Future<bool> future2;
+- promise2.associate(future2);
+- future2.discard();
+- ASSERT_TRUE(promise2.future().isDiscarded());
+-
+- Promise<bool> promise3;
+- Promise<bool> promise4;
+- promise3.associate(promise4.future());
+- promise4.fail("associate");
+- ASSERT_TRUE(promise3.future().isFailed());
+- EXPECT_EQ("associate", promise3.future().failure());
+-
+- // Test that 'discard' is associated in both directions.
+- Promise<bool> promise5;
+- Future<bool> future3;
+- promise5.associate(future3);
+- EXPECT_FALSE(future3.isDiscarded());
+- promise5.future().discard();
+- EXPECT_TRUE(future3.isDiscarded());
+-}
+-
+-
+-void onAny(const Future<bool>& future, bool* b)
+-{
+- ASSERT_TRUE(future.isReady());
+- *b = future.get();
+-}
+-
+-
+-TEST(Process, onAny)
+-{
+- bool b = false;
+- Future<bool>(true)
+- .onAny(std::tr1::bind(&onAny, std::tr1::placeholders::_1, &b));
+- EXPECT_TRUE(b);
+-}
+-
+-
+-Future<string> itoa1(int* const& i)
+-{
+- std::ostringstream out;
+- out << *i;
+- return out.str();
+-}
+-
+-
+-string itoa2(int* const& i)
+-{
+- std::ostringstream out;
+- out << *i;
+- return out.str();
+-}
+-
+-
+-TEST(Process, then)
+-{
+- Promise<int*> promise;
+-
+- int i = 42;
+-
+- promise.set(&i);
+-
+- Future<string> future = promise.future()
+- .then(std::tr1::bind(&itoa1, std::tr1::placeholders::_1));
+-
+- ASSERT_TRUE(future.isReady());
+- EXPECT_EQ("42", future.get());
+-
+- future = promise.future()
+- .then(std::tr1::bind(&itoa2, std::tr1::placeholders::_1));
+-
+- ASSERT_TRUE(future.isReady());
+- EXPECT_EQ("42", future.get());
+-}
+-
+-
+-Future<bool> readyFuture()
+-{
+- return true;
+-}
+-
+-
+-Future<bool> failedFuture()
+-{
+- return Failure("The value is not positive (or zero)");
+-}
+-
+-
+-Future<bool> pendingFuture(Future<bool>* future)
+-{
+- return *future; // Keep it pending.
+-}
+-
+-
+-Future<string> second(const bool& b)
+-{
+- return b ? string("true") : string("false");
+-}
+-
+-
+-Future<string> third(const string& s)
+-{
+- return s;
+-}
+-
+-
+-TEST(Process, chain)
+-{
+- Promise<int*> promise;
+-
+- Future<string> s = readyFuture()
+- .then(std::tr1::bind(&second, std::tr1::placeholders::_1))
+- .then(std::tr1::bind(&third, std::tr1::placeholders::_1));
+-
+- s.await();
+-
+- ASSERT_TRUE(s.isReady());
+- EXPECT_EQ("true", s.get());
+-
+- s = failedFuture()
+- .then(std::tr1::bind(&second, std::tr1::placeholders::_1))
+- .then(std::tr1::bind(&third, std::tr1::placeholders::_1));
+-
+- s.await();
+-
+- ASSERT_TRUE(s.isFailed());
+-
+- Future<bool> future;
+-
+- s = pendingFuture(&future)
+- .then(std::tr1::bind(&second, std::tr1::placeholders::_1))
+- .then(std::tr1::bind(&third, std::tr1::placeholders::_1));
+-
+- ASSERT_TRUE(s.isPending());
+- ASSERT_TRUE(future.isPending());
+-
+- s.discard();
+-
+- future.await();
+-
+- ASSERT_TRUE(future.isDiscarded());
+-}
+-
+-
+-class SpawnProcess : public Process<SpawnProcess>
+-{
+-public:
+- MOCK_METHOD0(initialize, void(void));
+- MOCK_METHOD0(finalize, void(void));
+-};
+-
+-
+-TEST(Process, spawn)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- SpawnProcess process;
+-
+- EXPECT_CALL(process, initialize())
+- .Times(1);
+-
+- EXPECT_CALL(process, finalize())
+- .Times(1);
+-
+- PID<SpawnProcess> pid = spawn(process);
+-
+- ASSERT_FALSE(!pid);
+-
+- ASSERT_FALSE(wait(pid, Seconds(0)));
+-
+- terminate(pid);
+- wait(pid);
+-}
+-
+-
+-class DispatchProcess : public Process<DispatchProcess>
+-{
+-public:
+- MOCK_METHOD0(func0, void(void));
+- MOCK_METHOD1(func1, bool(bool));
+- MOCK_METHOD1(func2, Future<bool>(bool));
+- MOCK_METHOD1(func3, int(int));
+- MOCK_METHOD2(func4, Future<bool>(bool, int));
+-};
+-
+-
+-TEST(Process, dispatch)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- DispatchProcess process;
+-
+- EXPECT_CALL(process, func0())
+- .Times(1);
+-
+- EXPECT_CALL(process, func1(_))
+- .WillOnce(ReturnArg<0>());
+-
+- EXPECT_CALL(process, func2(_))
+- .WillOnce(ReturnArg<0>());
+-
+- PID<DispatchProcess> pid = spawn(&process);
+-
+- ASSERT_FALSE(!pid);
+-
+- dispatch(pid, &DispatchProcess::func0);
+-
+- Future<bool> future;
+-
+- future = dispatch(pid, &DispatchProcess::func1, true);
+-
+- EXPECT_TRUE(future.get());
+-
+- future = dispatch(pid, &DispatchProcess::func2, true);
+-
+- EXPECT_TRUE(future.get());
+-
+- terminate(pid);
+- wait(pid);
+-}
+-
+-
+-TEST(Process, defer1)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- DispatchProcess process;
+-
+- EXPECT_CALL(process, func0())
+- .Times(1);
+-
+- EXPECT_CALL(process, func1(_))
+- .WillOnce(ReturnArg<0>());
+-
+- EXPECT_CALL(process, func2(_))
+- .WillOnce(ReturnArg<0>());
+-
+- EXPECT_CALL(process, func4(_, _))
+- .WillRepeatedly(ReturnArg<0>());
+-
+- PID<DispatchProcess> pid = spawn(&process);
+-
+- ASSERT_FALSE(!pid);
+-
+- {
+- Deferred<void(void)> func0 =
+- defer(pid, &DispatchProcess::func0);
+- func0();
+- }
+-
+- Future<bool> future;
+-
+- {
+- Deferred<Future<bool>(void)> func1 =
+- defer(pid, &DispatchProcess::func1, true);
+- future = func1();
+- EXPECT_TRUE(future.get());
+- }
+-
+- {
+- Deferred<Future<bool>(void)> func2 =
+- defer(pid, &DispatchProcess::func2, true);
+- future = func2();
+- EXPECT_TRUE(future.get());
+- }
+-
+- {
+- Deferred<Future<bool>(void)> func4 =
+- defer(pid, &DispatchProcess::func4, true, 42);
+- future = func4();
+- EXPECT_TRUE(future.get());
+- }
+-
+- {
+- Deferred<Future<bool>(bool)> func4 =
+- defer(pid, &DispatchProcess::func4, std::tr1::placeholders::_1, 42);
+- future = func4(false);
+- EXPECT_FALSE(future.get());
+- }
+-
+- {
+- Deferred<Future<bool>(int)> func4 =
+- defer(pid, &DispatchProcess::func4, true, std::tr1::placeholders::_1);
+- future = func4(42);
+- EXPECT_TRUE(future.get());
+- }
+-
+- // Only take const &!
+-
+- terminate(pid);
+- wait(pid);
+-}
+-
+-
+-class DeferProcess : public Process<DeferProcess>
+-{
+-public:
+- Future<string> func1(const Future<int>& f)
+- {
+- return f.then(defer(self(), &Self::_func1, std::tr1::placeholders::_1));
+- }
+-
+- Future<string> func2(const Future<int>& f)
+- {
+- return f.then(defer(self(), &Self::_func2));
+- }
+-
+-private:
+- Future<string> _func1(int i)
+- {
+- return stringify(i);
+- }
+-
+- Future<string> _func2()
+- {
+- return string("42");
+- }
+-};
+-
+-
+-TEST(Process, defer2)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- DeferProcess process;
+-
+- PID<DeferProcess> pid = spawn(process);
+-
+- Future<string> f = dispatch(pid, &DeferProcess::func1, 41);
+-
+- f.await();
+-
+- ASSERT_TRUE(f.isReady());
+- EXPECT_EQ("41", f.get());
+-
+- f = dispatch(pid, &DeferProcess::func2, 41);
+-
+- f.await();
+-
+- ASSERT_TRUE(f.isReady());
+- EXPECT_EQ("42", f.get());
+-
+- terminate(pid);
+- wait(pid);
+-}
+-
+-
+-template <typename T>
+-void set(T* t1, const T& t2)
+-{
+- *t1 = t2;
+-}
+-
+-
+-TEST(Process, defer3)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- volatile bool bool1 = false;
+- volatile bool bool2 = false;
+-
+- Deferred<void(bool)> set1 =
+- defer(std::tr1::function<void(bool)>(
+- std::tr1::bind(&set<volatile bool>,
+- &bool1,
+- std::tr1::placeholders::_1)));
+-
+- set1(true);
+-
+- Deferred<void(bool)> set2 =
+- defer(std::tr1::function<void(bool)>(
+- std::tr1::bind(&set<volatile bool>,
+- &bool2,
+- std::tr1::placeholders::_1)));
+-
+- set2(true);
+-
+- while (!bool1);
+- while (!bool2);
+-}
+-
+-
+-class HandlersProcess : public Process<HandlersProcess>
+-{
+-public:
+- HandlersProcess()
+- {
+- install("func", &HandlersProcess::func);
+- }
+-
+- MOCK_METHOD2(func, void(const UPID&, const string&));
+-};
+-
+-
+-TEST(Process, handlers)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- HandlersProcess process;
+-
+- EXPECT_CALL(process, func(_, _))
+- .Times(1);
+-
+- PID<HandlersProcess> pid = spawn(&process);
+-
+- ASSERT_FALSE(!pid);
+-
+- post(pid, "func");
+-
+- terminate(pid, false);
+- wait(pid);
+-}
+-
+-
+-// Tests EXPECT_MESSAGE and EXPECT_DISPATCH and in particular that an
+-// event can get dropped before being processed.
+-TEST(Process, expect)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- HandlersProcess process;
+-
+- EXPECT_CALL(process, func(_, _))
+- .Times(0);
+-
+- PID<HandlersProcess> pid = spawn(&process);
+-
+- ASSERT_FALSE(!pid);
+-
+- Future<Message> message = DROP_MESSAGE("func", _, _);
+-
+- post(pid, "func");
+-
+- AWAIT_EXPECT_READY(message);
+-
+- Future<Nothing> func = DROP_DISPATCH(pid, &HandlersProcess::func);
+-
+- dispatch(pid, &HandlersProcess::func, pid, "");
+-
+- AWAIT_EXPECT_READY(func);
+-
+- terminate(pid, false);
+- wait(pid);
+-}
+-
+-
+-// Tests the FutureArg<N> action.
+-TEST(Process, action)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- HandlersProcess process;
+-
+- PID<HandlersProcess> pid = spawn(&process);
+-
+- ASSERT_FALSE(!pid);
+-
+- Future<string> future1;
+- Future<Nothing> future2;
+- EXPECT_CALL(process, func(_, _))
+- .WillOnce(FutureArg<1>(&future1))
+- .WillOnce(FutureSatisfy(&future2));
+-
+- dispatch(pid, &HandlersProcess::func, pid, "hello world");
+-
+- AWAIT_EXPECT_EQ("hello world", future1);
+-
+- EXPECT_TRUE(future2.isPending());
+-
+- dispatch(pid, &HandlersProcess::func, pid, "hello world");
+-
+- AWAIT_EXPECT_READY(future2);
+-
+- terminate(pid, false);
+- wait(pid);
+-}
+-
+-
+-class BaseProcess : public Process<BaseProcess>
+-{
+-public:
+- virtual void func() = 0;
+- MOCK_METHOD0(foo, void());
+-};
+-
+-
+-class DerivedProcess : public BaseProcess
+-{
+-public:
+- DerivedProcess() {}
+- MOCK_METHOD0(func, void());
+-};
+-
+-
+-TEST(Process, inheritance)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- DerivedProcess process;
+-
+- EXPECT_CALL(process, func())
+- .Times(2);
+-
+- EXPECT_CALL(process, foo())
+- .Times(1);
+-
+- PID<DerivedProcess> pid1 = spawn(&process);
+-
+- ASSERT_FALSE(!pid1);
+-
+- dispatch(pid1, &DerivedProcess::func);
+-
+- PID<BaseProcess> pid2(process);
+- PID<BaseProcess> pid3 = pid1;
+-
+- ASSERT_EQ(pid2, pid3);
+-
+- dispatch(pid3, &BaseProcess::func);
+- dispatch(pid3, &BaseProcess::foo);
+-
+- terminate(pid1, false);
+- wait(pid1);
+-}
+-
+-
+-TEST(Process, thunk)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- struct Thunk
+- {
+- static int run(int i)
+- {
+- return i;
+- }
+-
+- static int run(int i, int j)
+- {
+- return run(i + j);
+- }
+- };
+-
+- int result = run(&Thunk::run, 21, 21).get();
+-
+- EXPECT_EQ(42, result);
+-}
+-
+-
+-class DelegatorProcess : public Process<DelegatorProcess>
+-{
+-public:
+- DelegatorProcess(const UPID& delegatee)
+- {
+- delegate("func", delegatee);
+- }
+-};
+-
+-
+-class DelegateeProcess : public Process<DelegateeProcess>
+-{
+-public:
+- DelegateeProcess()
+- {
+- install("func", &DelegateeProcess::func);
+- }
+-
+- MOCK_METHOD2(func, void(const UPID&, const string&));
+-};
+-
+-
+-TEST(Process, delegate)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- DelegateeProcess delegatee;
+- DelegatorProcess delegator(delegatee.self());
+-
+- EXPECT_CALL(delegatee, func(_, _))
+- .Times(1);
+-
+- spawn(&delegator);
+- spawn(&delegatee);
+-
+- post(delegator.self(), "func");
+-
+- terminate(delegator, false);
+- wait(delegator);
+-
+- terminate(delegatee, false);
+- wait(delegatee);
+-}
+-
+-
+-class TimeoutProcess : public Process<TimeoutProcess>
+-{
+-public:
+- MOCK_METHOD0(timeout, void());
+-};
+-
+-
+-TEST(Process, delay)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- Clock::pause();
+-
+- volatile bool timeoutCalled = false;
+-
+- TimeoutProcess process;
+-
+- EXPECT_CALL(process, timeout())
+- .WillOnce(Assign(&timeoutCalled, true));
+-
+- spawn(process);
+-
+- delay(Seconds(5), process.self(), &TimeoutProcess::timeout);
+-
+- Clock::advance(Seconds(5));
+-
+- while (!timeoutCalled);
+-
+- terminate(process);
+- wait(process);
+-
+- Clock::resume();
+-}
+-
+-
+-class OrderProcess : public Process<OrderProcess>
+-{
+-public:
+- void order(const PID<TimeoutProcess>& pid)
+- {
+- // TODO(benh): Add a test which uses 'send' instead of dispatch.
+- dispatch(pid, &TimeoutProcess::timeout);
+- }
+-};
+-
+-
+-TEST(Process, order)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- Clock::pause();
+-
+- TimeoutProcess process1;
+-
+- volatile bool timeoutCalled = false;
+-
+- EXPECT_CALL(process1, timeout())
+- .WillOnce(Assign(&timeoutCalled, true));
+-
+- spawn(process1);
+-
+- Time now = Clock::now(&process1);
+-
+- Seconds seconds(1);
+-
+- Clock::advance(Seconds(1));
+-
+- EXPECT_EQ(now, Clock::now(&process1));
+-
+- OrderProcess process2;
+- spawn(process2);
+-
+- dispatch(process2, &OrderProcess::order, process1.self());
+-
+- while (!timeoutCalled);
+-
+- EXPECT_EQ(now + seconds, Clock::now(&process1));
+-
+- terminate(process1);
+- wait(process1);
+-
+- terminate(process2);
+- wait(process2);
+-
+- Clock::resume();
+-}
+-
+-
+-class DonateProcess : public Process<DonateProcess>
+-{
+-public:
+- void donate()
+- {
+- DonateProcess process;
+- spawn(process);
+- terminate(process);
+- wait(process);
+- }
+-};
+-
+-
+-TEST(Process, donate)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- DonateProcess process;
+- spawn(process);
+-
+- dispatch(process, &DonateProcess::donate);
+-
+- terminate(process, false);
+- wait(process);
+-}
+-
+-
+-class ExitedProcess : public Process<ExitedProcess>
+-{
+-public:
+- ExitedProcess(const UPID& pid) { link(pid); }
+-
+- MOCK_METHOD1(exited, void(const UPID&));
+-};
+-
+-
+-TEST(Process, exited)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- UPID pid = spawn(new ProcessBase(), true);
+-
+- ExitedProcess process(pid);
+-
+- volatile bool exitedCalled = false;
+-
+- EXPECT_CALL(process, exited(pid))
+- .WillOnce(Assign(&exitedCalled, true));
+-
+- spawn(process);
+-
+- terminate(pid);
+-
+- while (!exitedCalled);
+-
+- terminate(process);
+- wait(process);
+-}
+-
+-
+-TEST(Process, select)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- Promise<int> promise1;
+- Promise<int> promise2;
+- Promise<int> promise3;
+- Promise<int> promise4;
+-
+- std::set<Future<int> > futures;
+- futures.insert(promise1.future());
+- futures.insert(promise2.future());
+- futures.insert(promise3.future());
+- futures.insert(promise4.future());
+-
+- promise1.set(42);
+-
+- Future<Future<int> > future = select(futures);
+-
+- EXPECT_TRUE(future.await());
+- EXPECT_TRUE(future.isReady());
+- EXPECT_TRUE(future.get().isReady());
+- EXPECT_EQ(42, future.get().get());
+-}
+-
+-
+-TEST(Process, collect)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- // First ensure an empty list functions correctly.
+- std::list<Future<int> > empty;
+- Future<std::list<int> > future = collect(empty);
+- AWAIT_ASSERT_READY(future);
+- EXPECT_TRUE(future.get().empty());
+-
+- Promise<int> promise1;
+- Promise<int> promise2;
+- Promise<int> promise3;
+- Promise<int> promise4;
+-
+- std::list<Future<int> > futures;
+- futures.push_back(promise1.future());
+- futures.push_back(promise2.future());
+- futures.push_back(promise3.future());
+- futures.push_back(promise4.future());
+-
+- // Set them out-of-order.
+- promise4.set(4);
+- promise2.set(2);
+- promise1.set(1);
+- promise3.set(3);
+-
+- future = collect(futures);
+-
+- AWAIT_ASSERT_READY(future);
+-
+- std::list<int> values;
+- values.push_back(1);
+- values.push_back(2);
+- values.push_back(3);
+- values.push_back(4);
+-
+- // We expect them to be returned in the same order as the
+- // future list that was passed in.
+- EXPECT_EQ(values, future.get());
+-}
+-
+-
+-TEST(Process, await)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- // First ensure an empty list functions correctly.
+- std::list<Future<int> > empty;
+- Future<std::list<Future<int> > > future = await(empty);
+- AWAIT_ASSERT_READY(future);
+- EXPECT_TRUE(future.get().empty());
+-
+- Promise<int> promise1;
+- Promise<int> promise2;
+- Promise<int> promise3;
+- Promise<int> promise4;
+-
+- std::list<Future<int> > futures;
+- futures.push_back(promise1.future());
+- futures.push_back(promise2.future());
+- futures.push_back(promise3.future());
+- futures.push_back(promise4.future());
+-
+- // Set them out-of-order.
+- promise4.set(4);
+- promise2.set(2);
+- promise1.set(1);
+- promise3.set(3);
+-
+- future = await(futures);
+-
+- AWAIT_ASSERT_READY(future);
+-
+- EXPECT_EQ(futures.size(), future.get().size());
+-
+- // We expect them to be returned in the same order as the
+- // future list that was passed in.
+- int i = 1;
+- foreach (const Future<int>& result, future.get()) {
+- ASSERT_TRUE(result.isReady());
+- ASSERT_EQ(i++, result.get());
+- }
+-}
+-
+-
+-class SettleProcess : public Process<SettleProcess>
+-{
+-public:
+- SettleProcess() : calledDispatch(false) {}
+-
+- virtual void initialize()
+- {
+- os::sleep(Milliseconds(10));
+- delay(Seconds(0), self(), &SettleProcess::afterDelay);
+- }
+-
+- void afterDelay()
+- {
+- dispatch(self(), &SettleProcess::afterDispatch);
+- os::sleep(Milliseconds(10));
+- TimeoutProcess timeoutProcess;
+- spawn(timeoutProcess);
+- terminate(timeoutProcess);
+- wait(timeoutProcess);
+- }
+-
+- void afterDispatch()
+- {
+- os::sleep(Milliseconds(10));
+- calledDispatch = true;
+- }
+-
+- volatile bool calledDispatch;
+-};
+-
+-
+-TEST(Process, settle)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- Clock::pause();
+- SettleProcess process;
+- spawn(process);
+- Clock::settle();
+- ASSERT_TRUE(process.calledDispatch);
+- terminate(process);
+- wait(process);
+- Clock::resume();
+-}
+-
+-
+-TEST(Process, pid)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- TimeoutProcess process;
+-
+- PID<TimeoutProcess> pid = process;
+-}
+-
+-
+-class Listener1 : public Process<Listener1>
+-{
+-public:
+- virtual void event1() = 0;
+-};
+-
+-
+-class Listener2 : public Process<Listener2>
+-{
+-public:
+- virtual void event2() = 0;
+-};
+-
+-
+-class MultipleListenerProcess
+- : public Process<MultipleListenerProcess>,
+- public Listener1,
+- public Listener2
+-{
+-public:
+- MOCK_METHOD0(event1, void());
+- MOCK_METHOD0(event2, void());
+-};
+-
+-
+-TEST(Process, listener)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- MultipleListenerProcess process;
+-
+- EXPECT_CALL(process, event1())
+- .Times(1);
+-
+- EXPECT_CALL(process, event2())
+- .Times(1);
+-
+- spawn(process);
+-
+- dispatch(PID<Listener1>(process), &Listener1::event1);
+- dispatch(PID<Listener2>(process), &Listener2::event2);
+-
+- terminate(process, false);
+- wait(process);
+-}
+-
+-
+-class EventReceiver
+-{
+-public:
+- MOCK_METHOD1(event1, void(int));
+- MOCK_METHOD1(event2, void(const string&));
+-};
+-
+-
+-TEST(Process, executor)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- volatile bool event1Called = false;
+- volatile bool event2Called = false;
+-
+- EventReceiver receiver;
+-
+- EXPECT_CALL(receiver, event1(42))
+- .WillOnce(Assign(&event1Called, true));
+-
+- EXPECT_CALL(receiver, event2("event2"))
+- .WillOnce(Assign(&event2Called, true));
+-
+- Executor executor;
+-
+- Deferred<void(int)> event1 =
+- executor.defer(std::tr1::bind(&EventReceiver::event1,
+- &receiver,
+- std::tr1::placeholders::_1));
+-
+- event1(42);
+-
+- Deferred<void(const string&)> event2 =
+- executor.defer(std::tr1::bind(&EventReceiver::event2,
+- &receiver,
+- std::tr1::placeholders::_1));
+-
+- event2("event2");
+-
+- while (!event1Called);
+- while (!event2Called);
+-}
+-
+-
+-class RemoteProcess : public Process<RemoteProcess>
+-{
+-public:
+- RemoteProcess()
+- {
+- install("handler", &RemoteProcess::handler);
+- }
+-
+- MOCK_METHOD2(handler, void(const UPID&, const string&));
+-};
+-
+-
+-TEST(Process, remote)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- RemoteProcess process;
+-
+- volatile bool handlerCalled = false;
+-
+- EXPECT_CALL(process, handler(_, _))
+- .WillOnce(Assign(&handlerCalled, true));
+-
+- spawn(process);
+-
+- int s = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
+-
+- ASSERT_LE(0, s);
+-
+- sockaddr_in addr;
+- memset(&addr, 0, sizeof(addr));
+- addr.sin_family = PF_INET;
+- addr.sin_port = htons(process.self().port);
+- addr.sin_addr.s_addr = process.self().ip;
+-
+- ASSERT_EQ(0, connect(s, (sockaddr*) &addr, sizeof(addr)));
+-
+- Message message;
+- message.name = "handler";
+- message.from = UPID();
+- message.to = process.self();
+-
+- const string& data = MessageEncoder::encode(&message);
+-
+- ASSERT_EQ(data.size(), write(s, data.data(), data.size()));
+-
+- ASSERT_EQ(0, close(s));
+-
+- while (!handlerCalled);
+-
+- terminate(process);
+- wait(process);
+-}
+-
+-
+-int foo()
+-{
+- return 1;
+-}
+-
+-int foo1(int a)
+-{
+- return a;
+-}
+-
+-
+-int foo2(int a, int b)
+-{
+- return a + b;
+-}
+-
+-
+-int foo3(int a, int b, int c)
+-{
+- return a + b + c;
+-}
+-
+-
+-int foo4(int a, int b, int c, int d)
+-{
+- return a + b + c + d;
+-}
+-
+-
+-void bar(int a)
+-{
+- return;
+-}
+-
+-
+-TEST(Process, async)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- // Non-void functions with different no.of args.
+- EXPECT_EQ(1, async(&foo).get());
+- EXPECT_EQ(10, async(&foo1, 10).get());
+- EXPECT_EQ(30, async(&foo2, 10, 20).get());
+- EXPECT_EQ(60, async(&foo3, 10, 20, 30).get());
+- EXPECT_EQ(100, async(&foo4, 10, 20, 30, 40).get());
+-
+- // Non-void function with a complex arg.
+- int i = 42;
+- EXPECT_EQ("42", async(&itoa2, &i).get());
+-
+- // Non-void function that returns a future.
+- EXPECT_EQ("42", async(&itoa1, &i).get().get());
+-}
+-
+-
+-TEST(Process, limiter)
+-{
+- ASSERT_TRUE(GTEST_IS_THREADSAFE);
+-
+- int permits = 2;
+- Duration duration = Milliseconds(5);
+-
+- RateLimiter limiter(permits, duration);
+- Milliseconds interval = duration / permits;
+-
+- Stopwatch stopwatch;
+- stopwatch.start();
+-
+- Future<Nothing> acquire1 = limiter.acquire();
+- Future<Nothing> acquire2 = limiter.acquire();
+- Future<Nothing> acquire3 = limiter.acquire();
+-
+- AWAIT_READY(acquire1);
+-
+- AWAIT_READY(acquire2);
+- ASSERT_LE(interval, stopwatch.elapsed());
+-
+- AWAIT_READY(acquire3);
+- ASSERT_LE(interval * 2, stopwatch.elapsed());
+-}
+-
+-
+-class FileServer : public Process<FileServer>
+-{
+-public:
+- FileServer(const string& _path)
+- : path(_path) {}
+-
+- virtual void initialize()
+- {
+- provide("", path);
+- }
+-
+- const string path;
+-};
+-
+-
+-TEST(Process, provide)
+-{
+- const Try<string>& mkdtemp = os::mkdtemp();
+- ASSERT_SOME(mkdtemp);
+-
+- const string LOREM_IPSUM =
+- "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
+- "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad "
+- "minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip "
+- "ex ea commodo consequat. Duis aute irure dolor in reprehenderit in "
+- "voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur "
+- "sint occaecat cupidatat non proident, sunt in culpa qui officia "
+- "deserunt mollit anim id est laborum.";
+-
+- const string path = path::join(mkdtemp.get(), "lorem.txt");
+- ASSERT_SOME(os::write(path, LOREM_IPSUM));
+-
+- FileServer server(path);
+- PID<FileServer> pid = spawn(server);
+-
+- Future<http::Response> response = http::get(pid);
+-
+- AWAIT_READY(response);
+-
+- ASSERT_EQ(LOREM_IPSUM, response.get().body);
+-
+- terminate(server);
+- wait(server);
+-
+- ASSERT_SOME(os::rmdir(path));
+-}
+diff --git a/3rdparty/libprocess/src/tests/shared_tests.cpp b/3rdparty/libprocess/src/tests/shared_tests.cpp
+deleted file mode 100644
+index 1df67b4..0000000
+--- a/3rdparty/libprocess/src/tests/shared_tests.cpp
++++ /dev/null
+@@ -1,109 +0,0 @@
+-#include <gmock/gmock.h>
+-
+-#include <process/gtest.hpp>
+-#include <process/owned.hpp>
+-#include <process/shared.hpp>
+-
+-using namespace process;
+-
+-class Foo
+-{
+-public:
+- int get() const { return value; }
+- void set(int _value) { value = _value; }
+-
+-private:
+- int value;
+-};
+-
+-
+-TEST(Shared, ConstAccess)
+-{
+- Foo* foo = new Foo();
+- foo->set(10);
+-
+- Shared<Foo> shared(foo);
+-
+- EXPECT_EQ(10, shared->get());
+-
+- // The following won't compile.
+- // shared->set(20);
+-}
+-
+-
+-TEST(Shared, Null)
+-{
+- Shared<Foo> shared(NULL);
+- Shared<Foo> shared2(shared);
+-
+- EXPECT_TRUE(shared.get() == NULL);
+- EXPECT_TRUE(shared2.get() == NULL);
+-}
+-
+-
+-TEST(Shared, Reset)
+-{
+- Foo* foo = new Foo();
+- foo->set(42);
+-
+- Shared<Foo> shared(foo);
+- Shared<Foo> shared2(shared);
+-
+- EXPECT_FALSE(shared.unique());
+- EXPECT_FALSE(shared2.unique());
+- EXPECT_EQ(42, shared->get());
+- EXPECT_EQ(42, shared2->get());
+-
+- shared.reset();
+-
+- EXPECT_FALSE(shared.unique());
+- EXPECT_TRUE(shared.get() == NULL);
+-
+- EXPECT_TRUE(shared2.unique());
+- EXPECT_EQ(42, shared2->get());
+-}
+-
+-
+-TEST(Shared, Own)
+-{
+- Foo* foo = new Foo();
+- foo->set(42);
+-
+- Shared<Foo> shared(foo);
+-
+- EXPECT_EQ(42, shared->get());
+- EXPECT_EQ(42, (*shared).get());
+- EXPECT_EQ(42, shared.get()->get());
+- EXPECT_TRUE(shared.unique());
+-
+- Future<Owned<Foo> > future;
+-
+- {
+- Shared<Foo> shared2(shared);
+-
+- EXPECT_EQ(42, shared2->get());
+- EXPECT_EQ(42, (*shared2).get());
+- EXPECT_EQ(42, shared2.get()->get());
+- EXPECT_FALSE(shared2.unique());
+- EXPECT_FALSE(shared.unique());
+-
+- future = shared2.own();
+-
+- // A shared pointer will be reset after it called 'own'.
+- EXPECT_TRUE(shared2.get() == NULL);
+-
+- // Do not allow 'own' to be called twice.
+- AWAIT_FAILED(shared.own());
+-
+- // Not "owned" yet as 'shared' is still holding the reference.
+- EXPECT_TRUE(future.isPending());
+- }
+-
+- shared.reset();
+- AWAIT_READY(future);
+-
+- Owned<Foo> owned = future.get();
+- EXPECT_EQ(42, owned->get());
+- EXPECT_EQ(42, (*owned).get());
+- EXPECT_EQ(42, owned.get()->get());
+-}
+diff --git a/3rdparty/libprocess/src/tests/statistics_tests.cpp b/3rdparty/libprocess/src/tests/statistics_tests.cpp
+deleted file mode 100644
+index e6c9a1b..0000000
+--- a/3rdparty/libprocess/src/tests/statistics_tests.cpp
++++ /dev/null
+@@ -1,190 +0,0 @@
+-#include <gmock/gmock.h>
+-
+-#include <map>
+-
+-#include <process/clock.hpp>
+-#include <process/future.hpp>
+-#include <process/gtest.hpp>
+-#include <process/statistics.hpp>
+-#include <process/time.hpp>
+-
+-#include <stout/duration.hpp>
+-
+-using namespace process;
+-
+-using std::map;
+-
+-
+-TEST(Statistics, set)
+-{
+- Statistics statistics(Days(1));
+-
+- // Set one using Clock::now() implicitly.
+- statistics.set("test", "statistic", 3.0);
+-
+- // Set one using Clock::now() explicitly.
+- Time now = Clock::now();
+- statistics.set("test", "statistic", 4.0, now);
+-
+- Future<map<Time, double> > values =
+- statistics.timeseries("test", "statistic");
+-
+- AWAIT_ASSERT_READY(values);
+-
+- EXPECT_EQ(2, values.get().size());
+-
+- EXPECT_GE(Clock::now(), values.get().begin()->first);
+- EXPECT_DOUBLE_EQ(3.0, values.get().begin()->second);
+-
+- EXPECT_EQ(1, values.get().count(now));
+- EXPECT_DOUBLE_EQ(4.0, values.get()[now]);
+-}
+-
+-
+-TEST(Statistics, truncate)
+-{
+- Clock::pause();
+-
+- Statistics statistics(Days(1));
+-
+- statistics.set("test", "statistic", 3.0);
+-
+- Future<map<Time, double> > values =
+- statistics.timeseries("test", "statistic");
+-
+- AWAIT_ASSERT_READY(values);
+-
+- EXPECT_EQ(1, values.get().size());
+- EXPECT_GE(Clock::now(), values.get().begin()->first);
+- EXPECT_DOUBLE_EQ(3.0, values.get().begin()->second);
+-
+- Clock::advance(Days(1) + Seconds(1));
+- Clock::settle();
+-
+- statistics.increment("test", "statistic");
+-
+- values = statistics.timeseries("test", "statistic");
+-
+- AWAIT_ASSERT_READY(values);
+-
+- EXPECT_EQ(1, values.get().size());
+- EXPECT_GE(Clock::now(), values.get().begin()->first);
+- EXPECT_DOUBLE_EQ(4.0, values.get().begin()->second);
+-
+- Clock::resume();
+-}
+-
+-
+-TEST(Statistics, meter) {
+- Statistics statistics(Days(1));
+-
+- // Set up a meter, and ensure it captures the expected time rate.
+- Future<Try<Nothing> > meter =
+- statistics.meter(
+- "test",
+- "statistic",
+- Owned<meters::Meter>(new meters::TimeRate("metered")));
+-
+- AWAIT_ASSERT_READY(meter);
+-
+- ASSERT_TRUE(meter.get().isSome());
+-
+- Time now = Clock::now();
+- statistics.set("test", "statistic", 1.0, now);
+- statistics.set("test", "statistic", 2.0, Time(now + Seconds(1)));
+- statistics.set("test", "statistic", 4.0, Time(now + Seconds(2)));
+-
+- // Check the raw statistic values.
+- Future<map<Time, double> > values =
+- statistics.timeseries("test", "statistic");
+-
+- AWAIT_ASSERT_READY(values);
+-
+- EXPECT_EQ(3, values.get().size());
+- EXPECT_EQ(1, values.get().count(now));
+- EXPECT_EQ(1, values.get().count(Time(now + Seconds(1))));
+- EXPECT_EQ(1, values.get().count(Time(now + Seconds(2))));
+-
+- EXPECT_EQ(1.0, values.get()[now]);
+- EXPECT_EQ(2.0, values.get()[Time(now + Seconds(1))]);
+- EXPECT_EQ(4.0, values.get()[Time(now + Seconds(2))]);
+-
+- // Now check the metered values.
+- values = statistics.timeseries("test", "metered");
+-
+- AWAIT_ASSERT_READY(values);
+-
+- EXPECT_EQ(2, values.get().size());
+- EXPECT_EQ(1, values.get().count(Time(now + Seconds(1))));
+- EXPECT_EQ(1, values.get().count(Time(now + Seconds(2))));
+-
+- EXPECT_EQ(0., values.get()[now]);
+- EXPECT_EQ(1.0, values.get()[Time(now + Seconds(1))]); // 100%.
+- EXPECT_EQ(2.0, values.get()[Time(now + Seconds(2))]); // 200%.
+-}
+-
+-
+-TEST(Statistics, archive)
+-{
+- Clock::pause();
+-
+- Statistics statistics(Seconds(10));
+-
+- // Create a meter and a statistic for archival.
+- // Set up a meter, and ensure it captures the expected time rate.
+- Future<Try<Nothing> > meter =
+- statistics.meter(
+- "test",
+- "statistic",
+- Owned<meters::Meter>(new meters::TimeRate("metered")));
+-
+- AWAIT_ASSERT_READY(meter);
+-
+- ASSERT_TRUE(meter.get().isSome());
+-
+- Time now = Clock::now();
+- statistics.set("test", "statistic", 1.0, now);
+- statistics.set("test", "statistic", 2.0, Time(now + Seconds(1)));
+-
+- // Archive and ensure the following:
+- // 1. The statistic will no longer be part of the snapshot.
+- // 2. Any meters associated with this statistic will be removed.
+- // 3. However, the time series will be retained until the window expiration.
+- statistics.archive("test", "statistic");
+-
+- // TODO(bmahler): Wait for JSON parsing to verify number 1.
+-
+- // Ensure the raw time series is present.
+- Future<map<Time, double> > values =
+- statistics.timeseries("test", "statistic");
+- AWAIT_ASSERT_READY(values);
+- EXPECT_FALSE(values.get().empty());
+-
+- // Ensure the metered timeseries is present.
+- values = statistics.timeseries("test", "metered");
+- AWAIT_ASSERT_READY(values);
+- EXPECT_FALSE(values.get().empty());
+-
+- // Expire the window and ensure the statistics were removed.
+- Clock::advance(STATISTICS_TRUNCATION_INTERVAL);
+- Clock::settle();
+-
+- // Ensure the raw statistics are gone.
+- values = statistics.timeseries("test", "statistic");
+- AWAIT_ASSERT_READY(values);
+- EXPECT_TRUE(values.get().empty());
+-
+- // Ensure the metered statistics are gone.
+- values = statistics.timeseries("test", "metered");
+- AWAIT_ASSERT_READY(values);
+- EXPECT_TRUE(values.get().empty());
+-
+- // Reactivate the statistic, and make sure the meter is still missing.
+- statistics.set("test", "statistic", 1.0, now);
+-
+- values = statistics.timeseries("test", "metered");
+- AWAIT_ASSERT_READY(values);
+- EXPECT_TRUE(values.get().empty());
+-
+- Clock::resume();
+-}
+diff --git a/3rdparty/libprocess/src/tests/time_tests.cpp b/3rdparty/libprocess/src/tests/time_tests.cpp
+deleted file mode 100644
+index a25827e..0000000
+--- a/3rdparty/libprocess/src/tests/time_tests.cpp
++++ /dev/null
+@@ -1,46 +0,0 @@
+-#include <gtest/gtest.h>
+-
+-#include <gmock/gmock.h>
+-
+-#include <process/clock.hpp>
+-#include <process/time.hpp>
+-
+-#include <stout/duration.hpp>
+-#include <stout/gtest.hpp>
+-#include <stout/os.hpp>
+-
+-using namespace process;
+-
+-
+-TEST(TimeTest, Arithmetic)
+-{
+- Time t = Time::EPOCH + Weeks(1000);
+- t -= Weeks(1);
+- EXPECT_EQ(Time::EPOCH + Weeks(999), t);
+-
+- t += Weeks(2);
+- EXPECT_EQ(Time::EPOCH + Weeks(1001), t);
+-
+- EXPECT_EQ(t, Time::EPOCH + Weeks(1000) + Weeks(1));
+- EXPECT_EQ(t, Time::EPOCH + Weeks(1002) - Weeks(1));
+-
+- EXPECT_EQ(Weeks(1), (Time::EPOCH + Weeks(1000)) - (Time::EPOCH + Weeks(999)));
+-}
+-
+-
+-TEST(TimeTest, Now)
+-{
+- Time t1 = Clock::now();
+- os::sleep(Microseconds(10));
+- ASSERT_LT(Microseconds(10), Clock::now() - t1);
+-}
+-
+-
+-TEST(TimeTest, Output)
+-{
+- EXPECT_EQ("1989-03-02 00:00:00+00:00", stringify(Time::EPOCH + Weeks(1000)));
+- EXPECT_EQ("1989-03-02 00:00:00.000000001+00:00",
+- stringify(Time::EPOCH + Weeks(1000) + Nanoseconds(1)));
+- EXPECT_EQ("1989-03-02 00:00:00.000001000+00:00",
+- stringify(Time::EPOCH + Weeks(1000) + Microseconds(1)));
+-}
+diff --git a/3rdparty/libprocess/src/timer.cpp b/3rdparty/libprocess/src/timer.cpp
+deleted file mode 100644
+index 63c5ac1..0000000
+--- a/3rdparty/libprocess/src/timer.cpp
++++ /dev/null
+@@ -1,56 +0,0 @@
+-#include <process/timer.hpp>
+-
+-#include "timeout.hpp"
+-
+-namespace process {
+-
+-class TimerProcess : public Process<TimerProcess>
+-{
+-public:
+- TimerProcess(double _secs,
+- const UPID& _pid,
+- std::tr1::function<void(ProcessBase*)>* _dispatcher)
+- : secs(_secs), pid(_pid), dispatcher(_dispatcher) {}
+-
+-protected:
+- virtual void operator () ()
+- {
+- if (receive(secs) == TIMEOUT) {
+- internal::dispatch(pid, dispatcher);
+- } else {
+- delete dispatcher;
+- }
+- }
+-
+-private:
+- const double secs;
+- const UPID pid;
+- std::tr1::function<void(ProcessBase*)>* dispatcher;
+-};
+-
+-
+-static void dispatch()
+-
+-
+-Timer::Timer(double secs,
+- const UPID& pid,
+- std::tr1::function<void(ProcessBase*)>* dispatcher)
+-{
+- timer = spawn(new TimerProcess(secs, pid, dispatcher), true);
+-}
+-
+-
+-Timer::~Timer()
+-{
+- // NOTE: Do not terminate the timer! Some users will simply ignore
+- // saving the timer because they never want to cancel, thus
+- // we can not terminate it here!
+-}
+-
+-
+-void Timer::cancel()
+-{
+- timeouts::cancel(timeout);
+-}
+-
+-} // namespace process {
+diff --git a/3rdparty/libprocess/src/timer.hpp b/3rdparty/libprocess/src/timer.hpp
+deleted file mode 100644
+index 443b5a0..0000000
+--- a/3rdparty/libprocess/src/timer.hpp
++++ /dev/null
+@@ -1,125 +0,0 @@
+-#ifndef TIMER_HPP
+-#define TIMER_HPP
+-
+-#include <ctime>
+-#include <iostream>
+-#include <iomanip>
+-
+-class timer
+-{
+- friend std::ostream& operator<<(std::ostream& os, timer& t);
+-
+-private:
+- bool running;
+- clock_t start_clock;
+- time_t start_time;
+- double acc_time;
+-
+- double elapsed_time();
+-
+-public:
+- // 'running' is initially false. A timer needs to be explicitly started
+- // using 'start' or 'restart'
+- timer() : running(false), start_clock(0), start_time(0), acc_time(0) { }
+-
+- void start(const char* msg = 0);
+- void restart(const char* msg = 0);
+- void stop(const char* msg = 0);
+- void check(const char* msg = 0);
+-
+-}; // class timer
+-
+-//===========================================================================
+-// Return the total time that the timer has been in the "running"
+-// state since it was first "started" or last "restarted". For
+-// "short" time periods (less than an hour), the actual cpu time
+-// used is reported instead of the elapsed time.
+-
+-inline double timer::elapsed_time()
+-{
+- time_t acc_sec = time(0) - start_time;
+- if (acc_sec < 3600)
+- return (clock() - start_clock) / (1.0 * CLOCKS_PER_SEC);
+- else
+- return (1.0 * acc_sec);
+-
+-} // timer::elapsed_time
+-
+-//===========================================================================
+-// Start a timer. If it is already running, let it continue running.
+-// Print an optional message.
+-
+-inline void timer::start(const char* msg)
+-{
+- // Print an optional message, something like "Starting timer t";
+- if (msg) std::cout << msg << std::endl;
+-
+- // Return immediately if the timer is already running
+- if (running) return;
+-
+- // Set timer status to running and set the start time
+- running = true;
+- start_clock = clock();
+- start_time = time(0);
+-
+-} // timer::start
+-
+-//===========================================================================
+-// Turn the timer off and start it again from 0. Print an optional message.
+-
+-inline void timer::restart(const char* msg)
+-{
+- // Print an optional message, something like "Restarting timer t";
+- if (msg) std::cout << msg << std::endl;
+-
+- // Set timer status to running, reset accumulated time, and set start time
+- running = true;
+- acc_time = 0;
+- start_clock = clock();
+- start_time = time(0);
+-
+-} // timer::restart
+-
+-//===========================================================================
+-// Stop the timer and print an optional message.
+-
+-inline void timer::stop(const char* msg)
+-{
+- // Print an optional message, something like "Stopping timer t";
+- if (msg) std::cout << msg << std::endl;
+-
+- // Compute accumulated running time and set timer status to not running
+- if (running) acc_time += elapsed_time();
+- running = false;
+-
+-} // timer::stop
+-
+-//===========================================================================
+-// Print out an optional message followed by the current timer timing.
+-
+-inline void timer::check(const char* msg)
+-{
+- // Print an optional message, something like "Checking timer t";
+- if (msg) std::cout << msg << " : ";
+-
+- std::cout << "Elapsed time [" << std::setiosflags(std::ios::fixed)
+- << std::setprecision(2)
+- << acc_time + (running ? elapsed_time() : 0) << "] seconds\n";
+-
+-} // timer::check
+-
+-//===========================================================================
+-// Allow timers to be printed to ostreams using the syntax 'os << t'
+-// for an ostream 'os' and a timer 't'. For example, "cout << t" will
+-// print out the total amount of time 't' has been "running".
+-
+-inline std::ostream& operator<<(std::ostream& os, timer& t)
+-{
+- os << std::setprecision(2) << std::setiosflags(std::ios::fixed)
+- << t.acc_time + (t.running ? t.elapsed_time() : 0);
+- return os;
+-}
+-
+-//===========================================================================
+-
+-#endif /* TIMER_HPP */
+diff --git a/include/mesos/process/async.hpp b/include/mesos/process/async.hpp
+new file mode 100644
+index 0000000..8fa2771
+--- /dev/null
++++ b/include/mesos/process/async.hpp
+@@ -0,0 +1,231 @@
++#ifndef __ASYNC_HPP__
++#define __ASYNC_HPP__
++
++#include <process/dispatch.hpp>
++#include <process/future.hpp>
++#include <process/id.hpp>
++#include <process/process.hpp>
++
++#include <tr1/functional>
++
++namespace process {
++
++// TODO(vinod): Merge this into ExecutorProcess.
++// TODO(vinod): Add support for void functions. Currently this is tricky,
++// because Future<void> is not supported.
++class AsyncExecutorProcess : public Process<AsyncExecutorProcess>
++{
++private:
++ friend class AsyncExecutor;
++
++ AsyncExecutorProcess() : ProcessBase(ID::generate("__async_executor__")) {}
++ virtual ~AsyncExecutorProcess() {}
++
++ // Not copyable, not assignable.
++ AsyncExecutorProcess(const AsyncExecutorProcess&);
++ AsyncExecutorProcess& operator = (const AsyncExecutorProcess&);
++
++ template<typename F>
++ typename std::tr1::result_of<F(void)>::type execute(
++ const F& f)
++ {
++ terminate(self()); // Terminate this process after the function returns.
++ return f();
++ }
++
++ // TODO(vinod): Use boost macro enumerations.
++ template<typename F, typename A1>
++ typename std::tr1::result_of<F(A1)>::type execute(
++ const F& f, A1 a1)
++ {
++ terminate(self()); // Terminate this process after the function returns.
++ return f(a1);
++ }
++
++ template<typename F, typename A1, typename A2>
++ typename std::tr1::result_of<F(A1, A2)>::type execute(
++ const F& f, A1 a1, A2 a2)
++ {
++ terminate(self()); // Terminate this process after the function returns.
++ return f(a1, a2);
++ }
++
++ template<typename F, typename A1, typename A2, typename A3>
++ typename std::tr1::result_of<F(A1, A2, A3)>::type execute(
++ const F& f, A1 a1, A2 a2, A3 a3)
++ {
++ terminate(self()); // Terminate this process after the function returns.
++ return f(a1, a2, a3);
++ }
++
++ template<typename F, typename A1, typename A2, typename A3, typename A4>
++ typename std::tr1::result_of<F(A1, A2, A3, A4)>::type execute(
++ const F& f, A1 a1, A2 a2, A3 a3, A4 a4)
++ {
++ terminate(self()); // Terminate this process after the function returns.
++ return f(a1, a2, a3, a4);
++ }
++};
++
++
++// This is a wrapper around AsyncExecutorProcess.
++class AsyncExecutor
++{
++private:
++ // Declare async functions as friends.
++ template<typename F>
++ friend Future<typename std::tr1::result_of<F(void)>::type> async(
++ const F& f);
++
++ template<typename F, typename A1>
++ friend Future<typename std::tr1::result_of<F(A1)>::type> async(
++ const F& f, A1 a1);
++
++ template<typename F, typename A1, typename A2>
++ friend Future<typename std::tr1::result_of<F(A1, A2)>::type> async(
++ const F& f, A1 a1, A2 a2);
++
++ template<typename F, typename A1, typename A2, typename A3>
++ friend Future<typename std::tr1::result_of<F(A1, A2, A3)>::type> async(
++ const F& f, A1 a1, A2 a2, A3 a3);
++
++ template<typename F, typename A1, typename A2, typename A3, typename A4>
++ friend Future<typename std::tr1::result_of<F(A1, A2, A3, A4)>::type> async(
++ const F& f, A1 a1, A2 a2, A3 a3, A4 a4);
++
++ AsyncExecutor()
++ {
++ process = new AsyncExecutorProcess();
++ spawn(process, true); // Automatically GC.
++ }
++
++ virtual ~AsyncExecutor() {}
++
++ // Not copyable, not assignable.
++ AsyncExecutor(const AsyncExecutor&);
++ AsyncExecutor& operator = (const AsyncExecutor&);
++
++ template<typename F>
++ Future<typename std::tr1::result_of<F(void)>::type> execute(
++ const F& f)
++ {
++ // Necessary to disambiguate.
++ typedef typename std::tr1::result_of<F(void)>::type
++ (AsyncExecutorProcess::*R)(const F&);
++
++ return dispatch(process,
++ static_cast<R>(&AsyncExecutorProcess::execute),
++ f);
++ }
++
++ // TODO(vinod): Use boost macro enumerations.
++ template<typename F, typename A1>
++ Future<typename std::tr1::result_of<F(A1)>::type> execute(
++ const F& f, A1 a1)
++ {
++ // Necessary to disambiguate.
++ typedef typename std::tr1::result_of<F(A1)>::type
++ (AsyncExecutorProcess::*R)(const F&, A1);
++
++ return dispatch(process,
++ static_cast<R>(&AsyncExecutorProcess::execute),
++ f,
++ a1);
++ }
++
++ template<typename F, typename A1, typename A2>
++ Future<typename std::tr1::result_of<F(A1, A2)>::type> execute(
++ const F& f, A1 a1, A2 a2)
++ {
++ // Necessary to disambiguate.
++ typedef typename std::tr1::result_of<F(A1, A2)>::type
++ (AsyncExecutorProcess::*R)(const F&, A1, A2);
++
++ return dispatch(process,
++ static_cast<R>(&AsyncExecutorProcess::execute),
++ f,
++ a1,
++ a2);
++ }
++
++ template<typename F, typename A1, typename A2, typename A3>
++ Future<typename std::tr1::result_of<F(A1, A2, A3)>::type> execute(
++ const F& f, A1 a1, A2 a2, A3 a3)
++ {
++ // Necessary to disambiguate.
++ typedef typename std::tr1::result_of<F(A1, A2, A3)>::type
++ (AsyncExecutorProcess::*R)(const F&, A1, A2, A3);
++
++ return dispatch(process,
++ static_cast<R>(&AsyncExecutorProcess::execute),
++ f,
++ a1,
++ a2,
++ a3);
++ }
++
++ template<typename F, typename A1, typename A2, typename A3, typename A4>
++ Future<typename std::tr1::result_of<F(A1, A2, A3, A4)>::type> execute(
++ const F& f, A1 a1, A2 a2, A3 a3, A4 a4)
++ {
++ // Necessary to disambiguate.
++ typedef typename std::tr1::result_of<F(A1, A2, A3, A4)>::type
++ (AsyncExecutorProcess::*R)(const F&, A1, A2, A3, A4);
++
++ return dispatch(process,
++ static_cast<R>(&AsyncExecutorProcess::execute),
++ f,
++ a1,
++ a2,
++ a3,
++ a4);
++ }
++
++ AsyncExecutorProcess* process;
++};
++
++
++// Provides an abstraction for asynchronously executing a function.
++// TODO(vinod): Use boost macro to enumerate arguments/params.
++template<typename F>
++Future<typename std::tr1::result_of<F(void)>::type>
++ async(const F& f)
++{
++ return AsyncExecutor().execute(f);
++}
++
++
++template<typename F, typename A1>
++Future<typename std::tr1::result_of<F(A1)>::type>
++ async(const F& f, A1 a1)
++{
++ return AsyncExecutor().execute(f, a1);
++}
++
++
++template<typename F, typename A1, typename A2>
++Future<typename std::tr1::result_of<F(A1, A2)>::type>
++ async(const F& f, A1 a1, A2 a2)
++{
++ return AsyncExecutor().execute(f, a1, a2);
++}
++
++
++template<typename F, typename A1, typename A2, typename A3>
++Future<typename std::tr1::result_of<F(A1, A2, A3)>::type>
++ async(const F& f, A1 a1, A2 a2, A3 a3)
++{
++ return AsyncExecutor().execute(f, a1, a2, a3);
++}
++
++
++template<typename F, typename A1, typename A2, typename A3, typename A4>
++Future<typename std::tr1::result_of<F(A1, A2, A3, A4)>::type>
++ async(const F& f, A1 a1, A2 a2, A3 a3, A4 a4)
++{
++ return AsyncExecutor().execute(f, a1, a2, a3, a4);
++}
++
++} // namespace process {
++
++#endif // __ASYNC_HPP__
+diff --git a/include/mesos/process/clock.hpp b/include/mesos/process/clock.hpp
+new file mode 100644
+index 0000000..82ae3c6
+--- /dev/null
++++ b/include/mesos/process/clock.hpp
+@@ -0,0 +1,32 @@
++#ifndef __PROCESS_CLOCK_HPP__
++#define __PROCESS_CLOCK_HPP__
++
++#include <process/time.hpp>
++
++#include <stout/duration.hpp>
++
++namespace process {
++
++// Forward declarations.
++class ProcessBase;
++class Time;
++
++class Clock
++{
++public:
++ static Time now();
++ static Time now(ProcessBase* process);
++ static void pause();
++ static bool paused();
++ static void resume();
++ static void advance(const Duration& duration);
++ static void advance(ProcessBase* process, const Duration& duration);
++ static void update(const Time& time);
++ static void update(ProcessBase* process, const Time& time);
++ static void order(ProcessBase* from, ProcessBase* to);
++ static void settle();
++};
++
++} // namespace process {
++
++#endif // __PROCESS_CLOCK_HPP__
+diff --git a/include/mesos/process/collect.hpp b/include/mesos/process/collect.hpp
+new file mode 100644
+index 0000000..27e2729
+--- /dev/null
++++ b/include/mesos/process/collect.hpp
+@@ -0,0 +1,235 @@
++#ifndef __PROCESS_COLLECT_HPP__
++#define __PROCESS_COLLECT_HPP__
++
++#include <assert.h>
++
++#include <list>
++
++#include <process/defer.hpp>
++#include <process/delay.hpp>
++#include <process/future.hpp>
++#include <process/process.hpp>
++#include <process/timeout.hpp>
++
++#include <stout/none.hpp>
++#include <stout/option.hpp>
++
++// TODO(bmahler): Move these into a futures.hpp header to group Future
++// related utilities.
++
++namespace process {
++
++// Waits on each future in the specified list and returns the list of
++// resulting values in the same order. If any future is discarded then
++// the result will be a failure. Likewise, if any future fails then
++// the result future will be a failure.
++template <typename T>
++Future<std::list<T> > collect(
++ std::list<Future<T> >& futures,
++ const Option<Timeout>& timeout = None());
++
++
++// Waits on each future in the specified set and returns the list of
++// non-pending futures. On timeout, the result will be a failure.
++template <typename T>
++Future<std::list<Future<T> > > await(
++ std::list<Future<T> >& futures,
++ const Option<Timeout>& timeout = None());
++
++
++namespace internal {
++
++template <typename T>
++class CollectProcess : public Process<CollectProcess<T> >
++{
++public:
++ CollectProcess(
++ const std::list<Future<T> >& _futures,
++ const Option<Timeout>& _timeout,
++ Promise<std::list<T> >* _promise)
++ : futures(_futures),
++ timeout(_timeout),
++ promise(_promise),
++ ready(0) {}
++
++ virtual ~CollectProcess()
++ {
++ delete promise;
++ }
++
++ virtual void initialize()
++ {
++ // Stop this nonsense if nobody cares.
++ promise->future().onDiscarded(defer(this, &CollectProcess::discarded));
++
++ // Only wait as long as requested.
++ if (timeout.isSome()) {
++ delay(timeout.get().remaining(), this, &CollectProcess::timedout);
++ }
++
++ typename std::list<Future<T> >::const_iterator iterator;
++ for (iterator = futures.begin(); iterator != futures.end(); ++iterator) {
++ (*iterator).onAny(
++ defer(this, &CollectProcess::waited, std::tr1::placeholders::_1));
++ }
++ }
++
++private:
++ void discarded()
++ {
++ terminate(this);
++ }
++
++ void timedout()
++ {
++ // Need to discard all of the futures so any of their associated
++ // resources can get properly cleaned up.
++ typename std::list<Future<T> >::const_iterator iterator;
++ for (iterator = futures.begin(); iterator != futures.end(); ++iterator) {
++ Future<T> future = *iterator; // Need a non-const copy to discard.
++ future.discard();
++ }
++
++ promise->fail("Collect failed: timed out");
++ terminate(this);
++ }
++
++ void waited(const Future<T>& future)
++ {
++ if (future.isFailed()) {
++ promise->fail("Collect failed: " + future.failure());
++ terminate(this);
++ } else if (future.isDiscarded()) {
++ promise->fail("Collect failed: future discarded");
++ terminate(this);
++ } else {
++ assert(future.isReady());
++ ready += 1;
++ if (ready == futures.size()) {
++ std::list<T> values;
++ foreach (const Future<T>& future, futures) {
++ values.push_back(future.get());
++ }
++ promise->set(values);
++ terminate(this);
++ }
++ }
++ }
++
++ const std::list<Future<T> > futures;
++ const Option<Timeout> timeout;
++ Promise<std::list<T> >* promise;
++ size_t ready;
++};
++
++
++template <typename T>
++class AwaitProcess : public Process<AwaitProcess<T> >
++{
++public:
++ AwaitProcess(
++ const std::list<Future<T> >& _futures,
++ const Option<Timeout>& _timeout,
++ Promise<std::list<Future<T> > >* _promise)
++ : futures(_futures),
++ timeout(_timeout),
++ promise(_promise),
++ ready(0) {}
++
++ virtual ~AwaitProcess()
++ {
++ delete promise;
++ }
++
++ virtual void initialize()
++ {
++ // Stop this nonsense if nobody cares.
++ promise->future().onDiscarded(defer(this, &AwaitProcess::discarded));
++
++ // Only wait as long as requested.
++ if (timeout.isSome()) {
++ delay(timeout.get().remaining(), this, &AwaitProcess::timedout);
++ }
++
++ typename std::list<Future<T> >::const_iterator iterator;
++ for (iterator = futures.begin(); iterator != futures.end(); ++iterator) {
++ (*iterator).onAny(
++ defer(this, &AwaitProcess::waited, std::tr1::placeholders::_1));
++ }
++ }
++
++private:
++ void discarded()
++ {
++ terminate(this);
++ }
++
++ void timedout()
++ {
++ // Need to discard all of the futures so any of their associated
++ // resources can get properly cleaned up.
++ typename std::list<Future<T> >::const_iterator iterator;
++ for (iterator = futures.begin(); iterator != futures.end(); ++iterator) {
++ Future<T> future = *iterator; // Need a non-const copy to discard.
++ future.discard();
++ }
++
++ promise->fail("Collect failed: timed out");
++ terminate(this);
++ }
++
++ void waited(const Future<T>& future)
++ {
++ assert(!future.isPending());
++
++ ready += 1;
++ if (ready == futures.size()) {
++ promise->set(futures);
++ terminate(this);
++ }
++ }
++
++ const std::list<Future<T> > futures;
++ const Option<Timeout> timeout;
++ Promise<std::list<Future<T> > >* promise;
++ size_t ready;
++};
++
++} // namespace internal {
++
++
++template <typename T>
++inline Future<std::list<T> > collect(
++ std::list<Future<T> >& futures,
++ const Option<Timeout>& timeout)
++{
++ if (futures.empty()) {
++ return std::list<T>();
++ }
++
++ Promise<std::list<T> >* promise = new Promise<std::list<T> >();
++ Future<std::list<T> > future = promise->future();
++ spawn(new internal::CollectProcess<T>(futures, timeout, promise), true);
++ return future;
++}
++
++
++template <typename T>
++inline Future<std::list<Future<T> > > await(
++ std::list<Future<T> >& futures,
++ const Option<Timeout>& timeout)
++{
++ if (futures.empty()) {
++ return futures;
++ }
++
++ Promise<std::list<Future<T> > >* promise =
++ new Promise<std::list<Future<T> > >();
++ Future<std::list<Future<T> > > future = promise->future();
++ spawn(new internal::AwaitProcess<T>(futures, timeout, promise), true);
++ return future;
++}
++
++} // namespace process {
++
++#endif // __PROCESS_COLLECT_HPP__
+diff --git a/include/mesos/process/defer.hpp b/include/mesos/process/defer.hpp
+new file mode 100644
+index 0000000..1eb770b
+--- /dev/null
++++ b/include/mesos/process/defer.hpp
+@@ -0,0 +1,438 @@
++#ifndef __PROCESS_DEFER_HPP__
++#define __PROCESS_DEFER_HPP__
++
++#include <tr1/functional>
++
++#include <process/deferred.hpp>
++#include <process/dispatch.hpp>
++#include <process/executor.hpp>
++
++#include <stout/preprocessor.hpp>
++
++namespace process {
++
++// The defer mechanism is very similar to the dispatch mechanism (see
++// dispatch.hpp), however, rather than scheduling the method to get
++// invoked, the defer mechanism returns a 'Deferred' object that when
++// invoked does the underlying dispatch. Similar to dispatch, we
++// provide the C++11 variadic template definitions first, and then use
++// Boost preprocessor macros to provide the actual definitions.
++
++
++// First, definitions of defer for methods returning void:
++//
++// template <typename T, typename ...P>
++// Deferred<void(void)> void defer(const PID<T>& pid,
++// void (T::*method)(P...),
++// P... p)
++// {
++// void (*dispatch)(const PID<T>&, void (T::*)(P...), P...) =
++// &process::template dispatch<T, P...>;
++
++// return Deferred<void(void)>(
++// std::tr1::bind(dispatch, pid, method, std::forward<P>(p)...));
++// }
++
++template <typename T>
++_Defer<void(*(PID<T>, void (T::*)(void)))
++ (const PID<T>&, void (T::*)(void))>
++defer(const PID<T>& pid, void (T::*method)(void))
++{
++ void (*dispatch)(const PID<T>&, void (T::*)(void)) =
++ &process::template dispatch<T>;
++ return std::tr1::bind(dispatch, pid, method);
++}
++
++template <typename T>
++_Defer<void(*(PID<T>, void (T::*)(void)))
++ (const PID<T>&, void (T::*)(void))>
++defer(const Process<T>& process, void (T::*method)(void))
++{
++ return defer(process.self(), method);
++}
++
++template <typename T>
++_Defer<void(*(PID<T>, void (T::*)(void)))
++ (const PID<T>&, void (T::*)(void))>
++defer(const Process<T>* process, void (T::*method)(void))
++{
++ return defer(process->self(), method);
++}
++
++#define TEMPLATE(Z, N, DATA) \
++ template <typename T, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ _Defer<void(*(PID<T>, \
++ void (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, A))) \
++ (const PID<T>&, \
++ void (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, P))> \
++ defer(const PID<T>& pid, \
++ void (T::*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ void (*dispatch)(const PID<T>&, \
++ void (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, P)) = \
++ &process::template dispatch<T, ENUM_PARAMS(N, P), ENUM_PARAMS(N, P)>; \
++ return std::tr1::bind(dispatch, pid, method, ENUM_PARAMS(N, a)); \
++ } \
++ \
++ template <typename T, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ _Defer<void(*(PID<T>, \
++ void (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, A))) \
++ (const PID<T>&, \
++ void (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, P))> \
++ defer(const Process<T>& process, \
++ void (T::*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ return defer(process.self(), method, ENUM_PARAMS(N, a)); \
++ } \
++ \
++ template <typename T, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ _Defer<void(*(PID<T>, \
++ void (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, A))) \
++ (const PID<T>&, \
++ void (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, P))> \
++ defer(const Process<T>* process, \
++ void (T::*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ return defer(process->self(), method, ENUM_PARAMS(N, a)); \
++ }
++
++ REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
++#undef TEMPLATE
++
++
++// Next, definitions of defer for methods returning future:
++//
++// template <typename R, typename T, typename ...P>
++// Deferred<Future<R>(void)> void defer(const PID<T>& pid,
++// Future<R> (T::*method)(P...),
++// P... p)
++// {
++// Future<R> (*dispatch)(const PID<T>&, Future<R> (T::*)(P...), P...) =
++// &process::template dispatch<R, T, P...>;
++//
++// return Deferred<Future<R>(void)>(
++// std::tr1::bind(dispatch, pid, method, std::forward<P>(p)...));
++// }
++
++template <typename R, typename T>
++_Defer<Future<R>(*(PID<T>, Future<R> (T::*)(void)))
++ (const PID<T>&, Future<R> (T::*)(void))>
++defer(const PID<T>& pid, Future<R> (T::*method)(void))
++{
++ Future<R> (*dispatch)(const PID<T>&, Future<R> (T::*)(void)) =
++ &process::template dispatch<R, T>;
++ return std::tr1::bind(dispatch, pid, method);
++}
++
++template <typename R, typename T>
++_Defer<Future<R>(*(PID<T>, Future<R> (T::*)(void)))(
++ const PID<T>&, Future<R> (T::*)(void))>
++defer(const Process<T>& process, Future<R> (T::*method)(void))
++{
++ return defer(process.self(), method);
++}
++
++template <typename R, typename T>
++_Defer<Future<R>(*(PID<T>, Future<R> (T::*)(void)))
++ (const PID<T>&, Future<R> (T::*)(void))>
++defer(const Process<T>* process, Future<R> (T::*method)(void))
++{
++ return defer(process->self(), method);
++}
++
++#define TEMPLATE(Z, N, DATA) \
++ template <typename R, \
++ typename T, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ _Defer<Future<R>(*(PID<T>, \
++ Future<R> (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, A))) \
++ (const PID<T>&, \
++ Future<R> (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, P))> \
++ defer(const PID<T>& pid, \
++ Future<R> (T::*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ Future<R> (*dispatch)(const PID<T>&, \
++ Future<R> (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, P)) = \
++ &process::template dispatch<R, T, ENUM_PARAMS(N, P), ENUM_PARAMS(N, P)>; \
++ return std::tr1::bind(dispatch, pid, method, ENUM_PARAMS(N, a)); \
++ } \
++ \
++ template <typename R, \
++ typename T, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ _Defer<Future<R>(*(PID<T>, \
++ Future<R> (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, A))) \
++ (const PID<T>&, \
++ Future<R> (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, P))> \
++ defer(const Process<T>& process, \
++ Future<R> (T::*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ return defer(process.self(), method, ENUM_PARAMS(N, a)); \
++ } \
++ \
++ template <typename R, \
++ typename T, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ _Defer<Future<R>(*(PID<T>, \
++ Future<R> (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, A))) \
++ (const PID<T>&, \
++ Future<R> (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, P))> \
++ defer(const Process<T>* process, \
++ Future<R> (T::*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ return defer(process->self(), method, ENUM_PARAMS(N, a)); \
++ }
++
++ REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
++#undef TEMPLATE
++
++
++// Next, definitions of defer for methods returning a value:
++//
++// template <typename R, typename T, typename ...P>
++// Deferred<Future<R>(void)> void defer(const PID<T>& pid,
++// R (T::*method)(P...),
++// P... p)
++// {
++// Future<R> (*dispatch)(const PID<T>&, R (T::*)(P...), P...) =
++// &process::template dispatch<R, T, P...>;
++//
++// return Deferred<Future<R>(void)>(
++// std::tr1::bind(dispatch, pid, method, std::forward<P>(p)...));
++// }
++
++template <typename R, typename T>
++_Defer<Future<R>(*(PID<T>, R (T::*)(void)))
++ (const PID<T>&, R (T::*)(void))>
++defer(const PID<T>& pid, R (T::*method)(void))
++{
++ Future<R> (*dispatch)(const PID<T>&, R (T::*)(void)) =
++ &process::template dispatch<R, T>;
++ return std::tr1::bind(dispatch, pid, method);
++}
++
++template <typename R, typename T>
++_Defer<Future<R>(*(PID<T>, R (T::*)(void)))
++ (const PID<T>&, R (T::*)(void))>
++defer(const Process<T>& process, R (T::*method)(void))
++{
++ return defer(process.self(), method);
++}
++
++template <typename R, typename T>
++_Defer<Future<R>(*(PID<T>, R (T::*)(void)))
++ (const PID<T>&, R (T::*)(void))>
++defer(const Process<T>* process, R (T::*method)(void))
++{
++ return defer(process->self(), method);
++}
++
++#define TEMPLATE(Z, N, DATA) \
++ template <typename R, \
++ typename T, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ _Defer<Future<R>(*(PID<T>, \
++ R (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, A))) \
++ (const PID<T>&, \
++ R (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, P))> \
++ defer(const PID<T>& pid, \
++ R (T::*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ Future<R> (*dispatch)(const PID<T>&, \
++ R (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, P)) = \
++ &process::template dispatch<R, T, ENUM_PARAMS(N, P), ENUM_PARAMS(N, P)>; \
++ return std::tr1::bind(dispatch, pid, method, ENUM_PARAMS(N, a)); \
++ } \
++ \
++ template <typename R, \
++ typename T, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ _Defer<Future<R>(*(PID<T>, \
++ R (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, A))) \
++ (const PID<T>&, \
++ R (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, P))> \
++ defer(const Process<T>& process, \
++ R (T::*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ return defer(process.self(), method, ENUM_PARAMS(N, a)); \
++ } \
++ \
++ template <typename R, \
++ typename T, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ _Defer<Future<R>(*(PID<T>, \
++ R (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, A))) \
++ (const PID<T>&, \
++ R (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, P))> \
++ defer(const Process<T>* process, \
++ R (T::*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ return defer(process->self(), method, ENUM_PARAMS(N, a)); \
++ }
++
++ REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
++#undef TEMPLATE
++
++
++namespace internal {
++
++inline void invoker(
++ ProcessBase* _,
++ const std::tr1::function<void(void)>& f)
++{
++ f();
++}
++
++inline void dispatcher(
++ const UPID& pid,
++ const std::tr1::function<void(void)>& f)
++{
++ std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > invoker(
++ new std::tr1::function<void(ProcessBase*)>(
++ std::tr1::bind(&internal::invoker,
++ std::tr1::placeholders::_1,
++ f)));
++
++ internal::dispatch(pid, invoker);
++}
++
++#define TEMPLATE(Z, N, DATA) \
++ template <ENUM_PARAMS(N, typename A)> \
++ void CAT(invoker, N)( \
++ ProcessBase* _, \
++ const std::tr1::function<void(ENUM_PARAMS(N, A))>& f, \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ f(ENUM_PARAMS(N, a)); \
++ } \
++ \
++ template <ENUM_PARAMS(N, typename A)> \
++ void CAT(dispatcher, N)( \
++ const UPID& pid, \
++ const std::tr1::function<void(ENUM_PARAMS(N, A))>& f, \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > invoker( \
++ new std::tr1::function<void(ProcessBase*)>( \
++ std::tr1::bind(&internal::CAT(invoker, N)<ENUM_PARAMS(N, A)>, \
++ std::tr1::placeholders::_1, \
++ f, \
++ ENUM_PARAMS(N, a)))); \
++ \
++ internal::dispatch(pid, invoker); \
++ }
++
++ REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
++#undef TEMPLATE
++
++
++ // We can't easily use 'std::tr1::_Placeholder<X>' when doing macro
++ // expansion via ENUM_BINARY_PARAMS because compilers don't like it
++ // when you try and concatenate '<' 'N' '>'. Thus, we typedef them.
++#define TEMPLATE(Z, N, DATA) \
++ typedef std::tr1::_Placeholder<INC(N)> _ ## N;
++
++ REPEAT(10, TEMPLATE, _)
++#undef TEMPLATE
++
++} // namespace internal {
++
++
++// Now we define defer calls for functions and bind statements.
++inline Deferred<void(void)> defer(const std::tr1::function<void(void)>& f)
++{
++ if (__process__ != NULL) {
++ // In C++11:
++ // const UPID pid = __process__->self();
++ // return []() {
++ // internal::dispatch(pid, [](ProcessBase* _) { f(); });
++ // }
++ return std::tr1::function<void(void)>(
++ std::tr1::bind(&internal::dispatcher,
++ __process__->self(),
++ f));
++ }
++
++ return __executor__->defer(f);
++}
++
++
++#define TEMPLATE(Z, N, DATA) \
++ template <ENUM_PARAMS(N, typename A)> \
++ Deferred<void(ENUM_PARAMS(N, A))> defer( \
++ const std::tr1::function<void(ENUM_PARAMS(N, A))>& f) \
++ { \
++ if (__process__ != NULL) { \
++ return std::tr1::function<void(ENUM_PARAMS(N, A))>( \
++ std::tr1::bind(&internal::CAT(dispatcher, N)<ENUM_PARAMS(N, A)>, \
++ __process__->self(), \
++ f, \
++ ENUM_BINARY_PARAMS(N, internal::_, () INTERCEPT))); \
++ } \
++ \
++ return __executor__->defer(f); \
++ } \
++ \
++ template <typename R, ENUM_PARAMS(N, typename A)> \
++ Deferred<Future<R>(ENUM_PARAMS(N, A))> defer( \
++ const std::tr1::function<Future<R>(ENUM_PARAMS(N, A))>& f) \
++ { \
++ if (__process__ != NULL) { \
++ return std::tr1::function<Future<R>(ENUM_PARAMS(N, A))>( \
++ std::tr1::bind(&internal::CAT(dispatcher, N)<ENUM_PARAMS(N, A)>, \
++ __process__->self(), \
++ f, \
++ ENUM_BINARY_PARAMS(N, internal::_, () INTERCEPT))); \
++ } \
++ \
++ return __executor__->defer(f); \
++ }
++
++ REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
++#undef TEMPLATE
++
++} // namespace process {
++
++#endif // __PROCESS_DEFER_HPP__
+diff --git a/include/mesos/process/deferred.hpp b/include/mesos/process/deferred.hpp
+new file mode 100644
+index 0000000..8907e80
+--- /dev/null
++++ b/include/mesos/process/deferred.hpp
+@@ -0,0 +1,136 @@
++#ifndef __PROCESS_DEFERRED_HPP__
++#define __PROCESS_DEFERRED_HPP__
++
++#include <tr1/functional>
++
++#include <process/future.hpp>
++#include <process/pid.hpp>
++
++#include <stout/preprocessor.hpp>
++
++namespace process {
++
++// Forward declarations (removing these produces cryptic compiler
++// errors even though we are just using them to declare friends).
++class Executor;
++template <typename _F> struct _Defer;
++
++
++// Acts like a function call but runs within an asynchronous execution
++// context such as an Executor or a ProcessBase (enforced because only
++// an executor or the 'defer' routines are allowed to create them).
++template <typename F>
++struct Deferred : std::tr1::function<F>
++{
++private:
++ // Only an Executor and the 'defer' routines can create these.
++ friend class Executor;
++
++ template <typename _F> friend struct _Defer;
++
++ friend Deferred<void(void)> defer(const std::tr1::function<void(void)>& f);
++
++#define TEMPLATE(Z, N, DATA) \
++ template <ENUM_PARAMS(N, typename A)> \
++ friend Deferred<void(ENUM_PARAMS(N, A))> defer( \
++ const std::tr1::function<void(ENUM_PARAMS(N, A))>& f);
++
++ REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
++#undef TEMPLATE
++
++ Deferred(const std::tr1::function<F>& f) : std::tr1::function<F>(f) {}
++};
++
++
++// The result of invoking the 'defer' routines is actually an internal
++// type, effectively just a wrapper around the result of invoking
++// 'std::tr1::bind'. However, we want the result of bind to be
++// castable to a 'Deferred' but we don't want anyone to be able to
++// create a 'Deferred' so we use a level-of-indirection via this type.
++template <typename F>
++struct _Defer : std::tr1::_Bind<F>
++{
++ template <typename _F>
++ operator Deferred<_F> ()
++ {
++ return Deferred<_F>(std::tr1::function<_F>(*this));
++ }
++
++private:
++ friend class Executor;
++
++ template <typename T>
++ friend _Defer<void(*(PID<T>, void (T::*)(void)))
++ (const PID<T>&, void (T::*)(void))>
++ defer(const PID<T>& pid, void (T::*method)(void));
++
++#define TEMPLATE(Z, N, DATA) \
++ template <typename T, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ friend _Defer<void(*(PID<T>, \
++ void (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, A))) \
++ (const PID<T>&, \
++ void (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, P))> \
++ defer(const PID<T>& pid, \
++ void (T::*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a));
++
++ REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
++#undef TEMPLATE
++
++ template <typename R, typename T>
++ friend _Defer<Future<R>(*(PID<T>, Future<R> (T::*)(void)))(
++ const PID<T>&, Future<R> (T::*)(void))>
++ defer(const PID<T>& pid, Future<R> (T::*method)(void));
++
++#define TEMPLATE(Z, N, DATA) \
++ template <typename R, \
++ typename T, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ friend _Defer<Future<R>(*(PID<T>, \
++ Future<R> (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, A))) \
++ (const PID<T>&, \
++ Future<R> (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, P))> \
++ defer(const PID<T>& pid, \
++ Future<R> (T::*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a));
++
++ REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
++#undef TEMPLATE
++
++ template <typename R, typename T>
++ friend _Defer<Future<R>(*(PID<T>, R (T::*)(void)))(
++ const PID<T>&, R (T::*)(void))>
++ defer(const PID<T>& pid, R (T::*method)(void));
++
++#define TEMPLATE(Z, N, DATA) \
++ template <typename R, \
++ typename T, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ friend _Defer<Future<R>(*(PID<T>, \
++ R (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, A))) \
++ (const PID<T>&, \
++ R (T::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, P))> \
++ defer(const PID<T>& pid, \
++ R (T::*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a));
++
++ REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
++#undef TEMPLATE
++
++ _Defer(const std::tr1::_Bind<F>& b)
++ : std::tr1::_Bind<F>(b) {}
++};
++
++} // namespace process {
++
++#endif // __PROCESS_DEFERRED_HPP__
+diff --git a/include/mesos/process/delay.hpp b/include/mesos/process/delay.hpp
+new file mode 100644
+index 0000000..97acd76
+--- /dev/null
++++ b/include/mesos/process/delay.hpp
+@@ -0,0 +1,119 @@
++#ifndef __PROCESS_DELAY_HPP__
++#define __PROCESS_DELAY_HPP__
++
++#include <tr1/functional>
++
++#include <process/dispatch.hpp>
++#include <process/timer.hpp>
++
++#include <stout/duration.hpp>
++#include <stout/preprocessor.hpp>
++
++namespace process {
++
++// The 'delay' mechanism enables you to delay a dispatch to a process
++// for some specified number of seconds. Returns a Timer instance that
++// can be cancelled (but it might have already executed or be
++// executing concurrently).
++
++template <typename T>
++Timer delay(const Duration& duration,
++ const PID<T>& pid,
++ void (T::*method)())
++{
++ std::tr1::shared_ptr<std::tr1::function<void(T*)> > thunk(
++ new std::tr1::function<void(T*)>(
++ std::tr1::bind(method, std::tr1::placeholders::_1)));
++
++ std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > dispatcher(
++ new std::tr1::function<void(ProcessBase*)>(
++ std::tr1::bind(&internal::vdispatcher<T>,
++ std::tr1::placeholders::_1,
++ thunk)));
++
++ std::tr1::function<void(void)> dispatch =
++ std::tr1::bind(internal::dispatch,
++ pid,
++ dispatcher,
++ internal::canonicalize(method));
++
++ return Timer::create(duration, dispatch);
++}
++
++
++template <typename T>
++Timer delay(const Duration& duration,
++ const Process<T>& process,
++ void (T::*method)())
++{
++ return delay(duration, process.self(), method);
++}
++
++
++template <typename T>
++Timer delay(const Duration& duration,
++ const Process<T>* process,
++ void (T::*method)())
++{
++ return delay(duration, process->self(), method);
++}
++
++
++#define TEMPLATE(Z, N, DATA) \
++ template <typename T, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ Timer delay(const Duration& duration, \
++ const PID<T>& pid, \
++ void (T::*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ std::tr1::shared_ptr<std::tr1::function<void(T*)> > thunk( \
++ new std::tr1::function<void(T*)>( \
++ std::tr1::bind(method, \
++ std::tr1::placeholders::_1, \
++ ENUM_PARAMS(N, a)))); \
++ \
++ std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > dispatcher( \
++ new std::tr1::function<void(ProcessBase*)>( \
++ std::tr1::bind(&internal::vdispatcher<T>, \
++ std::tr1::placeholders::_1, \
++ thunk))); \
++ \
++ std::tr1::function<void(void)> dispatch = \
++ std::tr1::bind(internal::dispatch, \
++ pid, \
++ dispatcher, \
++ internal::canonicalize(method)); \
++ \
++ return Timer::create(duration, dispatch); \
++ } \
++ \
++ template <typename T, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ Timer delay(const Duration& duration, \
++ const Process<T>& process, \
++ void (T::*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ return delay(duration, process.self(), method, ENUM_PARAMS(N, a)); \
++ } \
++ \
++ template <typename T, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ Timer delay(const Duration& duration, \
++ const Process<T>* process, \
++ void (T::*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ return delay(duration, process->self(), method, ENUM_PARAMS(N, a)); \
++ }
++
++ REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
++#undef TEMPLATE
++
++} // namespace process {
++
++#endif // __PROCESS_DELAY_HPP__
+diff --git a/include/mesos/process/dispatch.hpp b/include/mesos/process/dispatch.hpp
+new file mode 100644
+index 0000000..b337a87
+--- /dev/null
++++ b/include/mesos/process/dispatch.hpp
+@@ -0,0 +1,478 @@
++#ifndef __PROCESS_DISPATCH_HPP__
++#define __PROCESS_DISPATCH_HPP__
++
++#include <string>
++
++#include <tr1/functional>
++#include <tr1/memory> // TODO(benh): Replace all shared_ptr with unique_ptr.
++
++#include <process/process.hpp>
++
++#include <stout/preprocessor.hpp>
++
++namespace process {
++
++// The dispatch mechanism enables you to "schedule" a method to get
++// invoked on a process. The result of that method invocation is
++// accessible via the future that is returned by the dispatch method
++// (note, however, that it might not be the _same_ future as the one
++// returned from the method, if the method even returns a future, see
++// below). Assuming some class 'Fibonacci' has a (visible) method
++// named 'compute' that takes an integer, N (and returns the Nth
++// fibonacci number) you might use dispatch like so:
++//
++// PID<Fibonacci> pid = spawn(new Fibonacci(), true); // Use the GC.
++// Future<int> f = dispatch(pid, &Fibonacci::compute, 10);
++//
++// Because the pid argument is "typed" we can ensure that methods are
++// only invoked on processes that are actually of that type. Providing
++// this mechanism for varying numbers of function types and arguments
++// requires support for variadic templates, slated to be released in
++// C++11. Until then, we use the Boost preprocessor macros to
++// accomplish the same thing (all be it less cleanly). See below for
++// those definitions.
++//
++// Dispatching is done via a level of indirection. The dispatch
++// routine itself creates a promise that is passed as an argument to a
++// partially applied 'dispatcher' function (defined below). The
++// dispatcher routines get passed to the actual process via an
++// internal routine called, not suprisingly, 'dispatch', defined
++// below:
++
++namespace internal {
++
++// The internal dispatch routine schedules a function to get invoked
++// within the context of the process associated with the specified pid
++// (first argument), unless that process is no longer valid. Note that
++// this routine does not expect anything in particular about the
++// specified function (second argument). The semantics are simple: the
++// function gets applied/invoked with the process as its first
++// argument. Currently we wrap the function in a shared_ptr but this
++// will probably change in the future to unique_ptr (or a variant).
++void dispatch(
++ const UPID& pid,
++ const std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> >& f,
++ const std::string& method = std::string());
++
++// For each return type (void, future, value) there is a dispatcher
++// function which should complete the picture. Given the process
++// argument these routines downcast the process to the correct subtype
++// and invoke the thunk using the subtype as the argument
++// (receiver). Note that we must use dynamic_cast because we permit a
++// process to use multiple inheritance (e.g., to expose multiple
++// callback interfaces).
++
++template <typename T>
++void vdispatcher(
++ ProcessBase* process,
++ std::tr1::shared_ptr<std::tr1::function<void(T*)> > thunk)
++{
++ assert(process != NULL);
++ T* t = dynamic_cast<T*>(process);
++ assert(t != NULL);
++ (*thunk)(t);
++}
++
++
++template <typename R, typename T>
++void pdispatcher(
++ ProcessBase* process,
++ std::tr1::shared_ptr<std::tr1::function<Future<R>(T*)> > thunk,
++ std::tr1::shared_ptr<Promise<R> > promise)
++{
++ assert(process != NULL);
++ T* t = dynamic_cast<T*>(process);
++ assert(t != NULL);
++ promise->associate((*thunk)(t));
++}
++
++
++template <typename R, typename T>
++void rdispatcher(
++ ProcessBase* process,
++ std::tr1::shared_ptr<std::tr1::function<R(T*)> > thunk,
++ std::tr1::shared_ptr<Promise<R> > promise)
++{
++ assert(process != NULL);
++ T* t = dynamic_cast<T*>(process);
++ assert(t != NULL);
++ promise->set((*thunk)(t));
++}
++
++
++// Canonicalizes a pointer to a member function (i.e., method) into a
++// bytes representation for comparison (e.g., in tests).
++template <typename Method>
++std::string canonicalize(Method method)
++{
++ return std::string(reinterpret_cast<const char*>(&method), sizeof(method));
++}
++
++} // namespace internal {
++
++
++// Okay, now for the definition of the dispatch routines
++// themselves. For each routine we provide the version in C++11 using
++// variadic templates so the reader can see what the Boost
++// preprocessor macros are effectively providing. Using C++11 closures
++// would shorten these definitions even more.
++//
++// First, definitions of dispatch for methods returning void:
++//
++// template <typename T, typename ...P>
++// void dispatch(
++// const PID<T>& pid,
++// void (T::*method)(P...),
++// P... p)
++// {
++// std::tr1::shared_ptr<std::tr1::function<void(T*)> > thunk(
++// new std::tr1::function<void(T*)>(
++// std::tr1::bind(method,
++// std::tr1::placeholders::_1,
++// std::forward<P>(p)...)));
++//
++// std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > dispatcher(
++// new std::tr1::function<void(ProcessBase*)>(
++// std::tr1::bind(&internal::vdispatcher<T>,
++// std::tr1::placeholders::_1,
++// thunk)));
++//
++// internal::dispatch(pid, dispatcher, internal::canonicalize(method));
++// }
++
++template <typename T>
++void dispatch(
++ const PID<T>& pid,
++ void (T::*method)(void))
++{
++ std::tr1::shared_ptr<std::tr1::function<void(T*)> > thunk(
++ new std::tr1::function<void(T*)>(
++ std::tr1::bind(method, std::tr1::placeholders::_1)));
++
++ std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > dispatcher(
++ new std::tr1::function<void(ProcessBase*)>(
++ std::tr1::bind(&internal::vdispatcher<T>,
++ std::tr1::placeholders::_1,
++ thunk)));
++
++ internal::dispatch(pid, dispatcher, internal::canonicalize(method));
++}
++
++template <typename T>
++void dispatch(
++ const Process<T>& process,
++ void (T::*method)(void))
++{
++ dispatch(process.self(), method);
++}
++
++template <typename T>
++void dispatch(
++ const Process<T>* process,
++ void (T::*method)(void))
++{
++ dispatch(process->self(), method);
++}
++
++#define TEMPLATE(Z, N, DATA) \
++ template <typename T, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ void dispatch( \
++ const PID<T>& pid, \
++ void (T::*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ std::tr1::shared_ptr<std::tr1::function<void(T*)> > thunk( \
++ new std::tr1::function<void(T*)>( \
++ std::tr1::bind(method, \
++ std::tr1::placeholders::_1, \
++ ENUM_PARAMS(N, a)))); \
++ \
++ std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > dispatcher( \
++ new std::tr1::function<void(ProcessBase*)>( \
++ std::tr1::bind(&internal::vdispatcher<T>, \
++ std::tr1::placeholders::_1, \
++ thunk))); \
++ \
++ internal::dispatch(pid, dispatcher, internal::canonicalize(method)); \
++ } \
++ \
++ template <typename T, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ void dispatch( \
++ const Process<T>& process, \
++ void (T::*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ dispatch(process.self(), method, ENUM_PARAMS(N, a)); \
++ } \
++ \
++ template <typename T, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ void dispatch( \
++ const Process<T>* process, \
++ void (T::*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ dispatch(process->self(), method, ENUM_PARAMS(N, a)); \
++ }
++
++ REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
++#undef TEMPLATE
++
++
++// Next, definitions of methods returning a future:
++//
++// template <typename R, typename T, typename ...P>
++// Future<R> dispatch(
++// const PID<T>& pid,
++// Future<R> (T::*method)(P...),
++// P... p)
++// {
++// std::tr1::shared_ptr<std::tr1::function<Future<R>(T*)> > thunk(
++// new std::tr1::function<Future<R>(T*)>(
++// std::tr1::bind(method,
++// std::tr1::placeholders::_1,
++// std::forward<P>(p)...)));
++//
++// std::tr1::shared_ptr<Promise<R> > promise(new Promise<R>());
++// Future<R> future = promise->future();
++//
++// std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > dispatcher(
++// new std::tr1::function<void(ProcessBase*)>(
++// std::tr1::bind(&internal::pdispatcher<R, T>,
++// std::tr1::placeholders::_1,
++// thunk, promise)));
++//
++// internal::dispatch(pid, dispatcher, internal::canonicalize(method));
++//
++// return future;
++// }
++
++template <typename R, typename T>
++Future<R> dispatch(
++ const PID<T>& pid,
++ Future<R> (T::*method)(void))
++{
++ std::tr1::shared_ptr<std::tr1::function<Future<R>(T*)> > thunk(
++ new std::tr1::function<Future<R>(T*)>(
++ std::tr1::bind(method, std::tr1::placeholders::_1)));
++
++ std::tr1::shared_ptr<Promise<R> > promise(new Promise<R>());
++ Future<R> future = promise->future();
++
++ std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > dispatcher(
++ new std::tr1::function<void(ProcessBase*)>(
++ std::tr1::bind(&internal::pdispatcher<R, T>,
++ std::tr1::placeholders::_1,
++ thunk, promise)));
++
++ internal::dispatch(pid, dispatcher, internal::canonicalize(method));
++
++ return future;
++}
++
++template <typename R, typename T>
++Future<R> dispatch(
++ const Process<T>& process,
++ Future<R> (T::*method)(void))
++{
++ return dispatch(process.self(), method);
++}
++
++template <typename R, typename T>
++Future<R> dispatch(
++ const Process<T>* process,
++ Future<R> (T::*method)(void))
++{
++ return dispatch(process->self(), method);
++}
++
++#define TEMPLATE(Z, N, DATA) \
++ template <typename R, \
++ typename T, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ Future<R> dispatch( \
++ const PID<T>& pid, \
++ Future<R> (T::*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ std::tr1::shared_ptr<std::tr1::function<Future<R>(T*)> > thunk( \
++ new std::tr1::function<Future<R>(T*)>( \
++ std::tr1::bind(method, \
++ std::tr1::placeholders::_1, \
++ ENUM_PARAMS(N, a)))); \
++ \
++ std::tr1::shared_ptr<Promise<R> > promise(new Promise<R>()); \
++ Future<R> future = promise->future(); \
++ \
++ std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > dispatcher( \
++ new std::tr1::function<void(ProcessBase*)>( \
++ std::tr1::bind(&internal::pdispatcher<R, T>, \
++ std::tr1::placeholders::_1, \
++ thunk, promise))); \
++ \
++ internal::dispatch(pid, dispatcher, internal::canonicalize(method)); \
++ \
++ return future; \
++ } \
++ \
++ template <typename R, \
++ typename T, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ Future<R> dispatch( \
++ const Process<T>& process, \
++ Future<R> (T::*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ return dispatch(process.self(), method, ENUM_PARAMS(N, a)); \
++ } \
++ \
++ template <typename R, \
++ typename T, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ Future<R> dispatch( \
++ const Process<T>* process, \
++ Future<R> (T::*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ return dispatch(process->self(), method, ENUM_PARAMS(N, a)); \
++ }
++
++ REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
++#undef TEMPLATE
++
++
++// Next, definitions of methods returning a value.
++//
++// template <typename R, typename T, typename ...P>
++// Future<R> dispatch(
++// const PID<T>& pid,
++// R (T::*method)(P...),
++// P... p)
++// {
++// std::tr1::shared_ptr<std::tr1::function<R(T*)> > thunk(
++// new std::tr1::function<R(T*)>(
++// std::tr1::bind(method,
++// std::tr1::placeholders::_1,
++// std::forward<P>(p)...)));
++//
++// std::tr1::shared_ptr<Promise<R> > promise(new Promise<R>());
++// Future<R> future = promise->future();
++//
++// std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > dispatcher(
++// new std::tr1::function<void(ProcessBase*)>(
++// std::tr1::bind(&internal::rdispatcher<R, T>,
++// std::tr1::placeholders::_1,
++// thunk, promise)));
++//
++// internal::dispatch(pid, dispatcher, internal::canonicalize(method));
++//
++// return future;
++// }
++
++template <typename R, typename T>
++Future<R> dispatch(
++ const PID<T>& pid,
++ R (T::*method)(void))
++{
++ std::tr1::shared_ptr<std::tr1::function<R(T*)> > thunk(
++ new std::tr1::function<R(T*)>(
++ std::tr1::bind(method, std::tr1::placeholders::_1)));
++
++ std::tr1::shared_ptr<Promise<R> > promise(new Promise<R>());
++ Future<R> future = promise->future();
++
++ std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > dispatcher(
++ new std::tr1::function<void(ProcessBase*)>(
++ std::tr1::bind(&internal::rdispatcher<R, T>,
++ std::tr1::placeholders::_1,
++ thunk, promise)));
++
++ internal::dispatch(pid, dispatcher, internal::canonicalize(method));
++
++ return future;
++}
++
++template <typename R, typename T>
++Future<R> dispatch(
++ const Process<T>& process,
++ R (T::*method)(void))
++{
++ return dispatch(process.self(), method);
++}
++
++template <typename R, typename T>
++Future<R> dispatch(
++ const Process<T>* process,
++ R (T::*method)(void))
++{
++ return dispatch(process->self(), method);
++}
++
++#define TEMPLATE(Z, N, DATA) \
++ template <typename R, \
++ typename T, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ Future<R> dispatch( \
++ const PID<T>& pid, \
++ R (T::*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ std::tr1::shared_ptr<std::tr1::function<R(T*)> > thunk( \
++ new std::tr1::function<R(T*)>( \
++ std::tr1::bind(method, \
++ std::tr1::placeholders::_1, \
++ ENUM_PARAMS(N, a)))); \
++ \
++ std::tr1::shared_ptr<Promise<R> > promise(new Promise<R>()); \
++ Future<R> future = promise->future(); \
++ \
++ std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > dispatcher( \
++ new std::tr1::function<void(ProcessBase*)>( \
++ std::tr1::bind(&internal::rdispatcher<R, T>, \
++ std::tr1::placeholders::_1, \
++ thunk, promise))); \
++ \
++ internal::dispatch(pid, dispatcher, internal::canonicalize(method)); \
++ \
++ return future; \
++ } \
++ \
++ template <typename R, \
++ typename T, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ Future<R> dispatch( \
++ const Process<T>& process, \
++ R (T::*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ return dispatch(process.self(), method, ENUM_PARAMS(N, a)); \
++ } \
++ \
++ template <typename R, \
++ typename T, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ Future<R> dispatch( \
++ const Process<T>* process, \
++ R (T::*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ return dispatch(process->self(), method, ENUM_PARAMS(N, a)); \
++ }
++
++ REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
++#undef TEMPLATE
++
++} // namespace process {
++
++#endif // __PROCESS_DISPATCH_HPP__
+diff --git a/include/mesos/process/event.hpp b/include/mesos/process/event.hpp
+new file mode 100644
+index 0000000..cf728da
+--- /dev/null
++++ b/include/mesos/process/event.hpp
+@@ -0,0 +1,199 @@
++#ifndef __PROCESS_EVENT_HPP__
++#define __PROCESS_EVENT_HPP__
++
++#include <tr1/functional>
++#include <tr1/memory> // TODO(benh): Replace all shared_ptr with unique_ptr.
++
++#include <process/future.hpp>
++#include <process/http.hpp>
++#include <process/message.hpp>
++#include <process/socket.hpp>
++
++namespace process {
++
++// Forward declarations.
++class ProcessBase;
++struct MessageEvent;
++struct DispatchEvent;
++struct HttpEvent;
++struct ExitedEvent;
++struct TerminateEvent;
++
++
++struct EventVisitor
++{
++ virtual ~EventVisitor() {}
++ virtual void visit(const MessageEvent& event) {}
++ virtual void visit(const DispatchEvent& event) {}
++ virtual void visit(const HttpEvent& event) {}
++ virtual void visit(const ExitedEvent& event) {}
++ virtual void visit(const TerminateEvent& event) {}
++};
++
++
++struct Event
++{
++ virtual ~Event() {}
++
++ virtual void visit(EventVisitor* visitor) const = 0;
++
++ template <typename T>
++ bool is() const
++ {
++ bool result = false;
++ struct IsVisitor : EventVisitor
++ {
++ IsVisitor(bool* _result) : result(_result) {}
++ virtual void visit(const T& t) { *result = true; }
++ bool* result;
++ } visitor(&result);
++ visit(&visitor);
++ return result;
++ }
++
++ template <typename T>
++ const T& as() const
++ {
++ const T* result = NULL;
++ struct AsVisitor : EventVisitor
++ {
++ AsVisitor(const T** _result) : result(_result) {}
++ virtual void visit(const T& t) { *result = &t; }
++ const T** result;
++ } visitor(&result);
++ visit(&visitor);
++ if (result == NULL) {
++ std::cerr << "Attempting to \"cast\" event incorrectly!" << std::endl;
++ abort();
++ }
++ return *result;
++ }
++};
++
++
++struct MessageEvent : Event
++{
++ MessageEvent(Message* _message)
++ : message(_message) {}
++
++ virtual ~MessageEvent()
++ {
++ delete message;
++ }
++
++ virtual void visit(EventVisitor* visitor) const
++ {
++ visitor->visit(*this);
++ }
++
++ Message* const message;
++
++private:
++ // Not copyable, not assignable.
++ MessageEvent(const MessageEvent&);
++ MessageEvent& operator = (const MessageEvent&);
++};
++
++
++struct HttpEvent : Event
++{
++ HttpEvent(const Socket& _socket, http::Request* _request)
++ : socket(_socket), request(_request) {}
++
++ virtual ~HttpEvent()
++ {
++ delete request;
++ }
++
++ virtual void visit(EventVisitor* visitor) const
++ {
++ visitor->visit(*this);
++ }
++
++ const Socket socket;
++ http::Request* const request;
++
++private:
++ // Not copyable, not assignable.
++ HttpEvent(const HttpEvent&);
++ HttpEvent& operator = (const HttpEvent&);
++};
++
++
++struct DispatchEvent : Event
++{
++ DispatchEvent(
++ const UPID& _pid,
++ const std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> >& _f,
++ const std::string& _method)
++ : pid(_pid),
++ f(_f),
++ method(_method)
++ {}
++
++ virtual void visit(EventVisitor* visitor) const
++ {
++ visitor->visit(*this);
++ }
++
++ // PID receiving the dispatch.
++ const UPID pid;
++
++ // Function to get invoked as a result of this dispatch event.
++ const std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> > f;
++
++ // Canonical "byte" representation of a pointer to a member function
++ // (i.e., method) encapsulated in the above function (or empty if
++ // not applicable). Note that we use a byte representation because a
++ // pointer to a member function is not actually a pointer, but
++ // instead a POD.
++ // TODO(benh): Perform canonicalization lazily.
++ const std::string method;
++
++private:
++ // Not copyable, not assignable.
++ DispatchEvent(const DispatchEvent&);
++ DispatchEvent& operator = (const DispatchEvent&);
++};
++
++
++struct ExitedEvent : Event
++{
++ ExitedEvent(const UPID& _pid)
++ : pid(_pid) {}
++
++ virtual void visit(EventVisitor* visitor) const
++ {
++ visitor->visit(*this);
++ }
++
++ const UPID pid;
++
++private:
++ // Not copyable, not assignable.
++ ExitedEvent(const ExitedEvent&);
++ ExitedEvent& operator = (const ExitedEvent&);
++};
++
++
++struct TerminateEvent : Event
++{
++ TerminateEvent(const UPID& _from)
++ : from(_from) {}
++
++ virtual void visit(EventVisitor* visitor) const
++ {
++ visitor->visit(*this);
++ }
++
++ const UPID from;
++
++private:
++ // Not copyable, not assignable.
++ TerminateEvent(const TerminateEvent&);
++ TerminateEvent& operator = (const TerminateEvent&);
++};
++
++} // namespace event {
++
++#endif // __PROCESS_EVENT_HPP__
+diff --git a/include/mesos/process/executor.hpp b/include/mesos/process/executor.hpp
+new file mode 100644
+index 0000000..f203476
+--- /dev/null
++++ b/include/mesos/process/executor.hpp
+@@ -0,0 +1,260 @@
++#ifndef __PROCESS_EXECUTOR_HPP__
++#define __PROCESS_EXECUTOR_HPP__
++
++#include <process/deferred.hpp>
++#include <process/dispatch.hpp>
++#include <process/id.hpp>
++
++#include <stout/preprocessor.hpp>
++#include <stout/thread.hpp>
++
++namespace process {
++
++// Underlying "process" which handles invoking actual callbacks
++// created through an Executor.
++class ExecutorProcess : public Process<ExecutorProcess>
++{
++private:
++ friend class Executor;
++
++ ExecutorProcess() : ProcessBase(ID::generate("__executor__")) {}
++ virtual ~ExecutorProcess() {}
++
++ // Not copyable, not assignable.
++ ExecutorProcess(const ExecutorProcess&);
++ ExecutorProcess& operator = (const ExecutorProcess&);
++
++ // No arg invoke.
++ void invoke(const std::tr1::function<void(void)>& f) { f(); }
++
++ // Args invoke.
++#define TEMPLATE(Z, N, DATA) \
++ template <ENUM_PARAMS(N, typename A)> \
++ void CAT(invoke, N)( \
++ const std::tr1::function<void(ENUM_PARAMS(N, A))>& f, \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ f(ENUM_PARAMS(N, a)); \
++ }
++
++ REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
++#undef TEMPLATE
++};
++
++
++// Provides an abstraction that can take a standard function object
++// and convert it to a 'Deferred'. Each converted function object will
++// get invoked serially with respect to one another.
++class Executor
++{
++public:
++ Executor()
++ {
++ spawn(process);
++ }
++
++ ~Executor()
++ {
++ terminate(process);
++ wait(process);
++ }
++
++ void stop()
++ {
++ terminate(process);
++
++ // TODO(benh): Note that this doesn't wait because that could
++ // cause a deadlock ... thus, the semantics here are that no more
++ // dispatches will occur after this function returns but one may
++ // be occuring concurrently.
++ }
++
++ // We can't easily use 'std::tr1::_Placeholder<X>' when doing macro
++ // expansion via ENUM_BINARY_PARAMS because compilers don't like it
++ // when you try and concatenate '<' 'N' '>'. Thus, we typedef them.
++private:
++#define TEMPLATE(Z, N, DATA) \
++ typedef std::tr1::_Placeholder<INC(N)> _ ## N;
++
++ REPEAT(10, TEMPLATE, _)
++#undef TEMPLATE
++
++public:
++ // We provide wrappers for all standard function objects.
++ Deferred<void(void)> defer(
++ const std::tr1::function<void(void)>& f)
++ {
++ return Deferred<void(void)>(
++ std::tr1::bind(
++ &Executor::dispatcher,
++ process.self(), f));
++ }
++
++#define TEMPLATE(Z, N, DATA) \
++ template <ENUM_PARAMS(N, typename A)> \
++ Deferred<void(ENUM_PARAMS(N, A))> defer( \
++ const std::tr1::function<void(ENUM_PARAMS(N, A))>& f) \
++ { \
++ return Deferred<void(ENUM_PARAMS(N, A))>( \
++ std::tr1::bind( \
++ &Executor::CAT(dispatcher, N)<ENUM_PARAMS(N, A)>, \
++ process.self(), f, \
++ ENUM_BINARY_PARAMS(N, _, () INTERCEPT))); \
++ }
++
++ REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
++#undef TEMPLATE
++
++ // Unfortunately, it is currently difficult to "forward" type
++ // information from one result to another, so we must explicilty
++ // define wrappers for all std::tr1::bind results. First we start
++ // with the non-member std::tr1::bind results.
++ Deferred<void(void)> defer(
++ const std::tr1::_Bind<void(*(void))(void)>& b)
++ {
++ return defer(std::tr1::function<void(void)>(b));
++ }
++
++#define TEMPLATE(Z, N, DATA) \
++ template <ENUM_PARAMS(N, typename A)> \
++ Deferred<void(ENUM_PARAMS(N, A))> defer( \
++ const std::tr1::_Bind< \
++ void(*(ENUM_PARAMS(N, _))) \
++ (ENUM_PARAMS(N, A))>& b) \
++ { \
++ return defer(std::tr1::function<void(ENUM_PARAMS(N, A))>(b)); \
++ }
++
++ REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
++#undef TEMPLATE
++
++ // Now the member std::tr1::bind results:
++ // 1. Non-const member (function), non-const pointer (receiver).
++ // 2. Const member, non-const pointer.
++ // 3. Const member, const pointer.
++ // 4. Non-const member, non-const reference.
++ // 5. Const member, non-const reference.
++ // 6. Const member, const reference.
++ // 7. Non-const member, value.
++ // 8. Const member, value.
++#define TEMPLATE(Z, N, DATA) \
++ template <typename T ENUM_TRAILING_PARAMS(N, typename A)> \
++ Deferred<void(ENUM_PARAMS(N, A))> defer( \
++ const std::tr1::_Bind<std::tr1::_Mem_fn< \
++ void(T::*)(ENUM_PARAMS(N, A))> \
++ (T* ENUM_TRAILING_PARAMS(N, _))>& b) \
++ { \
++ return defer(std::tr1::function<void(ENUM_PARAMS(N, A))>(b)); \
++ } \
++ \
++ template <typename T ENUM_TRAILING_PARAMS(N, typename A)> \
++ Deferred<void(ENUM_PARAMS(N, A))> defer( \
++ const std::tr1::_Bind<std::tr1::_Mem_fn< \
++ void(T::*)(ENUM_PARAMS(N, A)) const> \
++ (T* ENUM_TRAILING_PARAMS(N, _))>& b) \
++ { \
++ return defer(std::tr1::function<void(ENUM_PARAMS(N, A))>(b)); \
++ } \
++ \
++ template <typename T ENUM_TRAILING_PARAMS(N, typename A)> \
++ Deferred<void(ENUM_PARAMS(N, A))> defer( \
++ const std::tr1::_Bind<std::tr1::_Mem_fn< \
++ void(T::*)(ENUM_PARAMS(N, A)) const> \
++ (const T* ENUM_TRAILING_PARAMS(N, _))>& b) \
++ { \
++ return defer(std::tr1::function<void(ENUM_PARAMS(N, A))>(b)); \
++ } \
++ \
++ template <typename T ENUM_TRAILING_PARAMS(N, typename A)> \
++ Deferred<void(ENUM_PARAMS(N, A))> defer( \
++ const std::tr1::_Bind<std::tr1::_Mem_fn< \
++ void(T::*)(ENUM_PARAMS(N, A))> \
++ (std::tr1::reference_wrapper<T> ENUM_TRAILING_PARAMS(N, _))>& b) \
++ { \
++ return defer(std::tr1::function<void(ENUM_PARAMS(N, A))>(b)); \
++ } \
++ \
++ template <typename T ENUM_TRAILING_PARAMS(N, typename A)> \
++ Deferred<void(ENUM_PARAMS(N, A))> defer( \
++ const std::tr1::_Bind<std::tr1::_Mem_fn< \
++ void(T::*)(ENUM_PARAMS(N, A)) const> \
++ (std::tr1::reference_wrapper<T> ENUM_TRAILING_PARAMS(N, _))>& b) \
++ { \
++ return defer(std::tr1::function<void(ENUM_PARAMS(N, A))>(b)); \
++ } \
++ \
++ template <typename T ENUM_TRAILING_PARAMS(N, typename A)> \
++ Deferred<void(ENUM_PARAMS(N, A))> defer( \
++ const std::tr1::_Bind<std::tr1::_Mem_fn< \
++ void(T::*)(ENUM_PARAMS(N, A)) const> \
++ (std::tr1::reference_wrapper<const T> ENUM_TRAILING_PARAMS(N, _))>& b) \
++ { \
++ return defer(std::tr1::function<void(ENUM_PARAMS(N, A))>(b)); \
++ } \
++ \
++ template <typename T ENUM_TRAILING_PARAMS(N, typename A)> \
++ Deferred<void(ENUM_PARAMS(N, A))> defer( \
++ const std::tr1::_Bind<std::tr1::_Mem_fn< \
++ void(T::*)(ENUM_PARAMS(N, A))> \
++ (T ENUM_TRAILING_PARAMS(N, _))>& b) \
++ { \
++ return defer(std::tr1::function<void(ENUM_PARAMS(N, A))>(b)); \
++ } \
++ \
++ template <typename T ENUM_TRAILING_PARAMS(N, typename A)> \
++ Deferred<void(ENUM_PARAMS(N, A))> defer( \
++ const std::tr1::_Bind<std::tr1::_Mem_fn< \
++ void(T::*)(ENUM_PARAMS(N, A)) const> \
++ (T ENUM_TRAILING_PARAMS(N, _))>& b) \
++ { \
++ return defer(std::tr1::function<void(ENUM_PARAMS(N, A))>(b)); \
++ }
++
++ REPEAT(11, TEMPLATE, _) // No args and args A0 -> A9.
++#undef TEMPLATE
++
++private:
++ // Not copyable, not assignable.
++ Executor(const Executor&);
++ Executor& operator = (const Executor&);
++
++ static void dispatcher(
++ const PID<ExecutorProcess>& pid,
++ const std::tr1::function<void(void)>& f)
++ {
++ // TODO(benh): Why not just use internal::dispatch?
++ dispatch(pid, &ExecutorProcess::invoke, f);
++ }
++
++#define TEMPLATE(Z, N, DATA) \
++ template <ENUM_PARAMS(N, typename A)> \
++ static void CAT(dispatcher, N)( \
++ const PID<ExecutorProcess>& pid, \
++ const std::tr1::function<void(ENUM_PARAMS(N, A))>& f, \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ dispatch( \
++ pid, \
++ &ExecutorProcess::CAT(invoke, N)<ENUM_PARAMS(N, A)>, \
++ f, ENUM_PARAMS(N, a)); \
++ }
++
++ REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
++#undef TEMPLATE
++
++ ExecutorProcess process;
++};
++
++
++// Per thread executor pointer. The extra level of indirection from
++// _executor_ to __executor__ is used in order to take advantage of
++// the ThreadLocal operators without needing the extra dereference as
++// well as lazily construct the actual executor.
++extern ThreadLocal<Executor>* _executor_;
++
++#define __executor__ \
++ (*_executor_ == NULL ? *_executor_ = new Executor() : *_executor_)
++
++} // namespace process {
++
++#endif // __PROCESS_EXECUTOR_HPP__
+diff --git a/include/mesos/process/filter.hpp b/include/mesos/process/filter.hpp
+new file mode 100644
+index 0000000..aa0c91b
+--- /dev/null
++++ b/include/mesos/process/filter.hpp
+@@ -0,0 +1,24 @@
++#ifndef __PROCESS_FILTER_HPP__
++#define __PROCESS_FILTER_HPP__
++
++#include <process/event.hpp>
++
++namespace process {
++
++class Filter {
++public:
++ virtual ~Filter() {}
++ virtual bool filter(const MessageEvent& event) { return false; }
++ virtual bool filter(const DispatchEvent& event) { return false; }
++ virtual bool filter(const HttpEvent& event) { return false; }
++ virtual bool filter(const ExitedEvent& event) { return false; }
++};
++
++
++// Use the specified filter on messages that get enqueued (note,
++// however, that you cannot filter timeout messages).
++void filter(Filter* filter);
++
++} // namespace process {
++
++#endif // __PROCESS_FILTER_HPP__
+diff --git a/include/mesos/process/future.hpp b/include/mesos/process/future.hpp
+new file mode 100644
+index 0000000..e473b3d
+--- /dev/null
++++ b/include/mesos/process/future.hpp
+@@ -0,0 +1,1023 @@
++#ifndef __PROCESS_FUTURE_HPP__
++#define __PROCESS_FUTURE_HPP__
++
++#include <assert.h>
++#include <stdlib.h> // For abort.
++
++#include <iostream>
++#include <list>
++#include <queue>
++#include <set>
++
++#include <glog/logging.h>
++
++#include <tr1/functional>
++#include <tr1/memory> // TODO(benh): Replace shared_ptr with unique_ptr.
++
++#include <process/latch.hpp>
++#include <process/pid.hpp>
++
++#include <stout/duration.hpp>
++#include <stout/error.hpp>
++#include <stout/option.hpp>
++#include <stout/preprocessor.hpp>
++
++namespace process {
++
++// Forward declaration (instead of include to break circular dependency).
++template <typename _F> struct _Defer;
++
++namespace internal {
++
++template <typename T>
++struct wrap;
++
++template <typename T>
++struct unwrap;
++
++} // namespace internal {
++
++
++// Forward declaration of Promise.
++template <typename T>
++class Promise;
++
++
++// Definition of a "shared" future. A future can hold any
++// copy-constructible value. A future is considered "shared" because
++// by default a future can be accessed concurrently.
++template <typename T>
++class Future
++{
++public:
++ // Constructs a failed future.
++ static Future<T> failed(const std::string& message);
++
++ Future();
++ Future(const T& _t);
++ Future(const Future<T>& that);
++ ~Future();
++
++ // Futures are assignable (and copyable). This results in the
++ // reference to the previous future data being decremented and a
++ // reference to 'that' being incremented.
++ Future<T>& operator = (const Future<T>& that);
++
++ // Comparision operators useful for using futures in collections.
++ bool operator == (const Future<T>& that) const;
++ bool operator < (const Future<T>& that) const;
++
++ // Helpers to get the current state of this future.
++ bool isPending() const;
++ bool isReady() const;
++ bool isDiscarded() const;
++ bool isFailed() const;
++
++ // Discards this future. This is similar to cancelling a future,
++ // however it also occurs when the last reference to this future
++ // gets cleaned up. Returns false if the future could not be
++ // discarded (for example, because it is ready or failed).
++ bool discard();
++
++ // Waits for this future to become ready, discarded, or failed.
++ bool await(const Duration& duration = Seconds(-1)) const;
++
++ // Return the value associated with this future, waits indefinitely
++ // until a value gets associated or until the future is discarded.
++ T get() const;
++
++ // Returns the failure message associated with this future.
++ std::string failure() const;
++
++ // Type of the callback functions that can get invoked when the
++ // future gets set, fails, or is discarded.
++ typedef std::tr1::function<void(const T&)> ReadyCallback;
++ typedef std::tr1::function<void(const std::string&)> FailedCallback;
++ typedef std::tr1::function<void(void)> DiscardedCallback;
++ typedef std::tr1::function<void(const Future<T>&)> AnyCallback;
++
++ // Installs callbacks for the specified events and returns a const
++ // reference to 'this' in order to easily support chaining.
++ const Future<T>& onReady(const ReadyCallback& callback) const;
++ const Future<T>& onFailed(const FailedCallback& callback) const;
++ const Future<T>& onDiscarded(const DiscardedCallback& callback) const;
++ const Future<T>& onAny(const AnyCallback& callback) const;
++
++ // Installs callbacks that get executed when this future is ready
++ // and associates the result of the callback with the future that is
++ // returned to the caller (which may be of a different type).
++ template <typename X>
++ Future<X> then(const std::tr1::function<Future<X>(const T&)>& f) const;
++
++ template <typename X>
++ Future<X> then(const std::tr1::function<X(const T&)>& f) const;
++
++ // Helpers for the compiler to be able to forward std::tr1::bind results.
++ template <typename X>
++ Future<X> then(const std::tr1::_Bind<X(*(void))(void)>& b) const
++ {
++ return then(std::tr1::function<X(const T&)>(b));
++ }
++
++#define TEMPLATE(Z, N, DATA) \
++ template <typename X, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ Future<X> then( \
++ const std::tr1::_Bind<X(*(ENUM_PARAMS(N, A))) \
++ (ENUM_PARAMS(N, P))>& b) const \
++ { \
++ return then(std::tr1::function<X(const T&)>(b)); \
++ }
++
++ REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
++#undef TEMPLATE
++
++ template <typename X>
++ Future<X> then(const std::tr1::_Bind<Future<X>(*(void))(void)>& b) const
++ {
++ return then(std::tr1::function<Future<X>(const T&)>(b));
++ }
++
++#define TEMPLATE(Z, N, DATA) \
++ template <typename X, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ Future<X> then( \
++ const std::tr1::_Bind<Future<X>(*(ENUM_PARAMS(N, A))) \
++ (ENUM_PARAMS(N, P))>& b) const \
++ { \
++ return then(std::tr1::function<Future<X>(const T&)>(b)); \
++ }
++
++ REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
++#undef TEMPLATE
++
++ // Helpers for the compiler to be able to forward 'defer' results.
++ template <typename X, typename U>
++ Future<X> then(const _Defer<Future<X>(*(PID<U>, X(U::*)(void)))
++ (const PID<U>&, X(U::*)(void))>& d) const
++ {
++ return then(std::tr1::function<Future<X>(const T&)>(d));
++ }
++
++#define TEMPLATE(Z, N, DATA) \
++ template <typename X, \
++ typename U, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ Future<X> then( \
++ const _Defer<Future<X>(*(PID<U>, \
++ X(U::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, A))) \
++ (const PID<U>&, \
++ X(U::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, P))>& d) const \
++ { \
++ return then(std::tr1::function<Future<X>(const T&)>(d)); \
++ }
++
++ REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
++#undef TEMPLATE
++
++ template <typename X, typename U>
++ Future<X> then(const _Defer<Future<X>(*(PID<U>, Future<X>(U::*)(void)))
++ (const PID<U>&, Future<X>(U::*)(void))>& d) const
++ {
++ return then(std::tr1::function<Future<X>(const T&)>(d));
++ }
++
++#define TEMPLATE(Z, N, DATA) \
++ template <typename X, \
++ typename U, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ Future<X> then( \
++ const _Defer<Future<X>(*(PID<U>, \
++ Future<X>(U::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, A))) \
++ (const PID<U>&, \
++ Future<X>(U::*)(ENUM_PARAMS(N, P)), \
++ ENUM_PARAMS(N, P))>& d) const \
++ { \
++ return then(std::tr1::function<Future<X>(const T&)>(d)); \
++ }
++
++ REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
++#undef TEMPLATE
++
++ // C++11 implementation (covers all functors).
++#if __cplusplus >= 201103L
++ template <typename F>
++ auto then(F f) const
++ -> typename internal::wrap<decltype(f(T()))>::Type;
++#endif
++
++private:
++ friend class Promise<T>;
++
++ // Sets the value for this future, unless the future is already set,
++ // failed, or discarded, in which case it returns false.
++ bool set(const T& _t);
++
++ // Sets this future as failed, unless the future is already set,
++ // failed, or discarded, in which case it returns false.
++ bool fail(const std::string& _message);
++
++ void copy(const Future<T>& that);
++ void cleanup();
++
++ enum State
++ {
++ PENDING,
++ READY,
++ FAILED,
++ DISCARDED,
++ };
++
++ struct Data
++ {
++ Data();
++ ~Data();
++
++ int lock;
++ Latch* latch;
++ State state;
++ T* t;
++ std::string* message; // Message associated with failure.
++ std::queue<ReadyCallback> onReadyCallbacks;
++ std::queue<FailedCallback> onFailedCallbacks;
++ std::queue<DiscardedCallback> onDiscardedCallbacks;
++ std::queue<AnyCallback> onAnyCallbacks;
++ };
++
++ std::tr1::shared_ptr<Data> data;
++};
++
++
++// Helper for creating failed futures.
++struct _Failure
++{
++ _Failure(const std::string& _message) : message(_message) {}
++
++ template <typename T>
++ operator Future<T> () const
++ {
++ return Future<T>::failed(message);
++ }
++
++ const std::string message;
++};
++
++
++inline _Failure Failure(const std::string& message)
++{
++ return _Failure(message);
++}
++
++
++inline _Failure Failure(const Error& error)
++{
++ return _Failure(error.message);
++}
++
++
++// TODO(benh): Make Promise a subclass of Future?
++template <typename T>
++class Promise
++{
++public:
++ Promise();
++ Promise(const T& t);
++ virtual ~Promise();
++
++ bool set(const T& _t);
++ bool set(const Future<T>& future); // Alias for associate.
++ bool associate(const Future<T>& future);
++ bool fail(const std::string& message);
++
++ // Returns a copy of the future associated with this promise.
++ Future<T> future() const;
++
++private:
++ // Not copyable, not assignable.
++ Promise(const Promise<T>&);
++ Promise<T>& operator = (const Promise<T>&);
++
++ Future<T> f;
++};
++
++
++template <>
++class Promise<void>;
++
++
++template <typename T>
++class Promise<T&>;
++
++
++template <typename T>
++Promise<T>::Promise() {}
++
++
++template <typename T>
++Promise<T>::Promise(const T& t)
++ : f(t) {}
++
++
++template <typename T>
++Promise<T>::~Promise() {}
++
++
++template <typename T>
++bool Promise<T>::set(const T& t)
++{
++ return f.set(t);
++}
++
++
++template <typename T>
++bool Promise<T>::set(const Future<T>& future)
++{
++ return associate(future);
++}
++
++
++template <typename T>
++bool Promise<T>::associate(const Future<T>& future)
++{
++ // TODO(jieyu): Make 'f' a true alias of 'future'. Currently, only
++ // 'discard' is associated in both directions. In other words, if a
++ // future gets discarded, the other future will also get discarded.
++ // For 'set' and 'fail', they are associated only in one direction.
++ // In other words, calling 'set' or 'fail' on this promise will not
++ // affect the result of the future that we associated.
++ f.onDiscarded(std::tr1::bind(&Future<T>::discard, future));
++
++ if (!f.isPending()) {
++ return false;
++ }
++
++ future
++ .onReady(std::tr1::bind(&Future<T>::set, f, std::tr1::placeholders::_1))
++ .onFailed(std::tr1::bind(&Future<T>::fail, f, std::tr1::placeholders::_1))
++ .onDiscarded(std::tr1::bind(&Future<T>::discard, f));
++
++ return true;
++}
++
++
++template <typename T>
++bool Promise<T>::fail(const std::string& message)
++{
++ return f.fail(message);
++}
++
++
++template <typename T>
++Future<T> Promise<T>::future() const
++{
++ return f;
++}
++
++
++// Internal helper utilities.
++namespace internal {
++
++template <typename T>
++struct wrap
++{
++ typedef Future<T> Type;
++};
++
++
++template <typename X>
++struct wrap<Future<X> >
++{
++ typedef Future<X> Type;
++};
++
++
++template <typename T>
++struct unwrap
++{
++ typedef T Type;
++};
++
++
++template <typename X>
++struct unwrap<Future<X> >
++{
++ typedef X Type;
++};
++
++
++inline void acquire(int* lock)
++{
++ while (!__sync_bool_compare_and_swap(lock, 0, 1)) {
++ asm volatile ("pause");
++ }
++}
++
++
++inline void release(int* lock)
++{
++ // Unlock via a compare-and-swap so we get a memory barrier too.
++ bool unlocked = __sync_bool_compare_and_swap(lock, 1, 0);
++ assert(unlocked);
++}
++
++
++template <typename T>
++void select(
++ const Future<T>& future,
++ std::tr1::shared_ptr<Promise<Future<T > > > promise)
++{
++ // We never fail the future associated with our promise.
++ assert(!promise->future().isFailed());
++
++ if (promise->future().isPending()) { // No-op if it's discarded.
++ if (future.isReady()) { // We only set the promise if a future is ready.
++ promise->set(future);
++ }
++ }
++}
++
++} // namespace internal {
++
++
++// TODO(benh): Move select and discard into 'futures' namespace.
++
++// Returns a future that captures any ready future in a set. Note that
++// select DOES NOT capture a future that has failed or been discarded.
++template <typename T>
++Future<Future<T> > select(const std::set<Future<T> >& futures)
++{
++ std::tr1::shared_ptr<Promise<Future<T> > > promise(
++ new Promise<Future<T> >());
++
++ Future<Future<T> > future = promise->future();
++
++ std::tr1::function<void(const Future<T>&)> select =
++ std::tr1::bind(&internal::select<T>,
++ std::tr1::placeholders::_1,
++ promise);
++
++ typename std::set<Future<T> >::iterator iterator;
++ for (iterator = futures.begin(); iterator != futures.end(); ++iterator) {
++ (*iterator).onAny(std::tr1::bind(select, std::tr1::placeholders::_1));
++ }
++
++ return future;
++}
++
++
++template <typename T>
++void discard(const std::set<Future<T> >& futures)
++{
++ typename std::set<Future<T> >::const_iterator iterator;
++ for (iterator = futures.begin(); iterator != futures.end(); ++iterator) {
++ Future<T> future = *iterator; // Need a non-const copy to discard.
++ future.discard();
++ }
++}
++
++
++template <typename T>
++void discard(const std::list<Future<T> >& futures)
++{
++ typename std::list<Future<T> >::const_iterator iterator;
++ for (iterator = futures.begin(); iterator != futures.end(); ++iterator) {
++ Future<T> future = *iterator; // Need a non-const copy to discard.
++ future.discard();
++ }
++}
++
++
++template <class T>
++void fail(const std::vector<Promise<T>*>& promises, const std::string& message)
++{
++ typename std::vector<Promise<T>*>::const_iterator iterator;
++ for (iterator = promises.begin(); iterator != promises.end(); ++iterator) {
++ Promise<T>* promise = *iterator;
++ promise->fail(message);
++ }
++}
++
++
++template <class T>
++void fail(const std::list<Promise<T>*>& promises, const std::string& message)
++{
++ typename std::list<Promise<T>*>::const_iterator iterator;
++ for (iterator = promises.begin(); iterator != promises.end(); ++iterator) {
++ Promise<T>* promise = *iterator;
++ promise->fail(message);
++ }
++}
++
++
++template <typename T>
++Future<T> Future<T>::failed(const std::string& message)
++{
++ Future<T> future;
++ future.fail(message);
++ return future;
++}
++
++
++template <typename T>
++Future<T>::Data::Data()
++ : lock(0),
++ latch(NULL),
++ state(PENDING),
++ t(NULL),
++ message(NULL) {}
++
++
++template <typename T>
++Future<T>::Data::~Data()
++{
++ delete latch;
++ delete t;
++ delete message;
++}
++
++
++template <typename T>
++Future<T>::Future()
++ : data(new Data()) {}
++
++
++template <typename T>
++Future<T>::Future(const T& _t)
++ : data(new Data())
++{
++ set(_t);
++}
++
++
++template <typename T>
++Future<T>::Future(const Future<T>& that)
++ : data(that.data) {}
++
++
++template <typename T>
++Future<T>::~Future()
++{
++ if (data.unique()) {
++ discard();
++ }
++}
++
++
++template <typename T>
++Future<T>& Future<T>::operator = (const Future<T>& that)
++{
++ if (this != &that) {
++ if (data.unique()) {
++ discard();
++ }
++ data = that.data;
++ }
++ return *this;
++}
++
++
++template <typename T>
++bool Future<T>::operator == (const Future<T>& that) const
++{
++ return data == that.data;
++}
++
++
++template <typename T>
++bool Future<T>::operator < (const Future<T>& that) const
++{
++ return data < that.data;
++}
++
++
++template <typename T>
++bool Future<T>::discard()
++{
++ bool result = false;
++
++ internal::acquire(&data->lock);
++ {
++ if (data->state == PENDING) {
++ data->state = DISCARDED;
++ if (data->latch != NULL) {
++ data->latch->trigger();
++ }
++ result = true;
++ }
++ }
++ internal::release(&data->lock);
++
++ // Invoke all callbacks associated with this future being
++ // DISCARDED. We don't need a lock because the state is now in
++ // DISCARDED so there should not be any concurrent modifications.
++ if (result) {
++ while (!data->onDiscardedCallbacks.empty()) {
++ // TODO(*): Invoke callbacks in another execution context.
++ data->onDiscardedCallbacks.front()();
++ data->onDiscardedCallbacks.pop();
++ }
++
++ while (!data->onAnyCallbacks.empty()) {
++ // TODO(*): Invoke callbacks in another execution context.
++ data->onAnyCallbacks.front()(*this);
++ data->onAnyCallbacks.pop();
++ }
++ }
++
++ return result;
++}
++
++
++template <typename T>
++bool Future<T>::isPending() const
++{
++ return data->state == PENDING;
++}
++
++
++template <typename T>
++bool Future<T>::isReady() const
++{
++ return data->state == READY;
++}
++
++
++template <typename T>
++bool Future<T>::isDiscarded() const
++{
++ return data->state == DISCARDED;
++}
++
++
++template <typename T>
++bool Future<T>::isFailed() const
++{
++ return data->state == FAILED;
++}
++
++
++template <typename T>
++bool Future<T>::await(const Duration& duration) const
++{
++ bool await = false;
++
++ internal::acquire(&data->lock);
++ {
++ if (data->state == PENDING) {
++ if (data->latch == NULL) {
++ data->latch = new Latch();
++ }
++ await = true;
++ }
++ }
++ internal::release(&data->lock);
++
++ if (await) {
++ return data->latch->await(duration);
++ }
++
++ return true;
++}
++
++
++template <typename T>
++T Future<T>::get() const
++{
++ if (!isReady()) {
++ await();
++ }
++
++ CHECK(!isPending()) << "Future was in PENDING after await()";
++
++ if (!isReady()) {
++ if (isFailed()) {
++ std::cerr << "Future::get() but state == FAILED: "
++ << failure() << std::endl;
++ } else if (isDiscarded()) {
++ std::cerr << "Future::get() but state == DISCARDED" << std::endl;
++ }
++ abort();
++ }
++
++ assert(data->t != NULL);
++ return *data->t;
++}
++
++
++template <typename T>
++std::string Future<T>::failure() const
++{
++ if (data->message != NULL) {
++ return *data->message;
++ }
++ return "";
++}
++
++
++template <typename T>
++const Future<T>& Future<T>::onReady(const ReadyCallback& callback) const
++{
++ bool run = false;
++
++ internal::acquire(&data->lock);
++ {
++ if (data->state == READY) {
++ run = true;
++ } else if (data->state == PENDING) {
++ data->onReadyCallbacks.push(callback);
++ }
++ }
++ internal::release(&data->lock);
++
++ // TODO(*): Invoke callback in another execution context.
++ if (run) {
++ callback(*data->t);
++ }
++
++ return *this;
++}
++
++
++template <typename T>
++const Future<T>& Future<T>::onFailed(const FailedCallback& callback) const
++{
++ bool run = false;
++
++ internal::acquire(&data->lock);
++ {
++ if (data->state == FAILED) {
++ run = true;
++ } else if (data->state == PENDING) {
++ data->onFailedCallbacks.push(callback);
++ }
++ }
++ internal::release(&data->lock);
++
++ // TODO(*): Invoke callback in another execution context.
++ if (run) {
++ callback(*data->message);
++ }
++
++ return *this;
++}
++
++
++template <typename T>
++const Future<T>& Future<T>::onDiscarded(
++ const DiscardedCallback& callback) const
++{
++ bool run = false;
++
++ internal::acquire(&data->lock);
++ {
++ if (data->state == DISCARDED) {
++ run = true;
++ } else if (data->state == PENDING) {
++ data->onDiscardedCallbacks.push(callback);
++ }
++ }
++ internal::release(&data->lock);
++
++ // TODO(*): Invoke callback in another execution context.
++ if (run) {
++ callback();
++ }
++
++ return *this;
++}
++
++
++template <typename T>
++const Future<T>& Future<T>::onAny(const AnyCallback& callback) const
++{
++ bool run = false;
++
++ internal::acquire(&data->lock);
++ {
++ if (data->state != PENDING) {
++ run = true;
++ } else if (data->state == PENDING) {
++ data->onAnyCallbacks.push(callback);
++ }
++ }
++ internal::release(&data->lock);
++
++ // TODO(*): Invoke callback in another execution context.
++ if (run) {
++ callback(*this);
++ }
++
++ return *this;
++}
++
++
++namespace internal {
++
++template <typename T, typename X>
++void thenf(const std::tr1::shared_ptr<Promise<X> >& promise,
++ const std::tr1::function<Future<X>(const T&)>& f,
++ const Future<T>& future)
++{
++ if (future.isReady()) {
++ promise->associate(f(future.get()));
++ } else if (future.isFailed()) {
++ promise->fail(future.failure());
++ } else if (future.isDiscarded()) {
++ promise->future().discard();
++ }
++}
++
++
++template <typename T, typename X>
++void then(const std::tr1::shared_ptr<Promise<X> >& promise,
++ const std::tr1::function<X(const T&)>& f,
++ const Future<T>& future)
++{
++ if (future.isReady()) {
++ promise->set(f(future.get()));
++ } else if (future.isFailed()) {
++ promise->fail(future.failure());
++ } else if (future.isDiscarded()) {
++ promise->future().discard();
++ }
++}
++
++} // namespace internal {
++
++
++template <typename T>
++template <typename X>
++Future<X> Future<T>::then(const std::tr1::function<Future<X>(const T&)>& f) const
++{
++ std::tr1::shared_ptr<Promise<X> > promise(new Promise<X>());
++
++ std::tr1::function<void(const Future<T>&)> thenf =
++ std::tr1::bind(&internal::thenf<T, X>,
++ promise,
++ f,
++ std::tr1::placeholders::_1);
++
++ onAny(thenf);
++
++ // Propagate discarding up the chain (note that we bind with a copy
++ // of this future since 'this' might no longer be valid but other
++ // references might still exist.
++ // TODO(benh): Need to pass 'future' as a weak_ptr so that we can
++ // avoid reference counting cycles!
++ std::tr1::function<void(void)> discard =
++ std::tr1::bind(&Future<T>::discard, *this);
++
++ promise->future().onDiscarded(discard);
++
++ return promise->future();
++}
++
++
++template <typename T>
++template <typename X>
++Future<X> Future<T>::then(const std::tr1::function<X(const T&)>& f) const
++{
++ std::tr1::shared_ptr<Promise<X> > promise(new Promise<X>());
++
++ std::tr1::function<void(const Future<T>&)> then =
++ std::tr1::bind(&internal::then<T, X>,
++ promise,
++ f,
++ std::tr1::placeholders::_1);
++
++ onAny(then);
++
++ // Propagate discarding up the chain (note that we bind with a copy
++ // of this future since 'this' might no longer be valid but other
++ // references might still exist.
++ // TODO(benh): Need to pass 'future' as a weak_ptr so that we can
++ // avoid reference counting cycles!
++ std::tr1::function<void(void)> discard =
++ std::tr1::bind(&Future<T>::discard, *this);
++
++ promise->future().onDiscarded(discard);
++
++ return promise->future();
++}
++
++
++#if __cplusplus >= 201103L
++template <typename T>
++template <typename F>
++auto Future<T>::then(F f) const
++ -> typename internal::wrap<decltype(f(T()))>::Type
++{
++ typedef typename internal::unwrap<decltype(f(T()))>::Type X;
++
++ std::tr1::shared_ptr<Promise<X>> promise(new Promise<X>());
++
++ onAny([=] (const Future<T>& future) {
++ if (future.isReady()) {
++ promise->set(f(future.get()));
++ } else if (future.isFailed()) {
++ promise->fail(future.failure());
++ } else if (future.isDiscarded()) {
++ promise->future().discard();
++ }
++ });
++
++ // TODO(benh): Need to use weak_ptr here so that we can avoid
++ // reference counting cycles!
++ Future<T> future(*this);
++
++ promise->future().onDiscarded([=] () {
++ future.discard(); // Need a non-const copy to discard.
++ });
++
++ return promise->future();
++}
++#endif
++
++
++template <typename T>
++bool Future<T>::set(const T& _t)
++{
++ bool result = false;
++
++ internal::acquire(&data->lock);
++ {
++ if (data->state == PENDING) {
++ data->t = new T(_t);
++ data->state = READY;
++ if (data->latch != NULL) {
++ data->latch->trigger();
++ }
++ result = true;
++ }
++ }
++ internal::release(&data->lock);
++
++ // Invoke all callbacks associated with this future being READY. We
++ // don't need a lock because the state is now in READY so there
++ // should not be any concurrent modications.
++ if (result) {
++ while (!data->onReadyCallbacks.empty()) {
++ // TODO(*): Invoke callbacks in another execution context.
++ data->onReadyCallbacks.front()(*data->t);
++ data->onReadyCallbacks.pop();
++ }
++
++ while (!data->onAnyCallbacks.empty()) {
++ // TODO(*): Invoke callbacks in another execution context.
++ data->onAnyCallbacks.front()(*this);
++ data->onAnyCallbacks.pop();
++ }
++ }
++
++ return result;
++}
++
++
++template <typename T>
++bool Future<T>::fail(const std::string& _message)
++{
++ bool result = false;
++
++ internal::acquire(&data->lock);
++ {
++ if (data->state == PENDING) {
++ data->message = new std::string(_message);
++ data->state = FAILED;
++ if (data->latch != NULL) {
++ data->latch->trigger();
++ }
++ result = true;
++ }
++ }
++ internal::release(&data->lock);
++
++ // Invoke all callbacks associated with this future being FAILED. We
++ // don't need a lock because the state is now in FAILED so there
++ // should not be any concurrent modications.
++ if (result) {
++ while (!data->onFailedCallbacks.empty()) {
++ // TODO(*): Invoke callbacks in another execution context.
++ data->onFailedCallbacks.front()(*data->message);
++ data->onFailedCallbacks.pop();
++ }
++
++ while (!data->onAnyCallbacks.empty()) {
++ // TODO(*): Invoke callbacks in another execution context.
++ data->onAnyCallbacks.front()(*this);
++ data->onAnyCallbacks.pop();
++ }
++ }
++
++ return result;
++}
++
++} // namespace process {
++
++#endif // __PROCESS_FUTURE_HPP__
+diff --git a/include/mesos/process/gc.hpp b/include/mesos/process/gc.hpp
+new file mode 100644
+index 0000000..e83c636
+--- /dev/null
++++ b/include/mesos/process/gc.hpp
+@@ -0,0 +1,46 @@
++#ifndef __PROCESS_GC_HPP__
++#define __PROCESS_GC_HPP__
++
++#include <map>
++
++#include <process/process.hpp>
++
++
++namespace process {
++
++class GarbageCollector : public Process<GarbageCollector>
++{
++public:
++ GarbageCollector() : ProcessBase("__gc__") {}
++ virtual ~GarbageCollector() {}
++
++ template <typename T>
++ void manage(const T* t)
++ {
++ const ProcessBase* process = t;
++ if (process != NULL) {
++ processes[process->self()] = process;
++ link(process->self());
++ }
++ }
++
++protected:
++ virtual void exited(const UPID& pid)
++ {
++ if (processes.count(pid) > 0) {
++ const ProcessBase* process = processes[pid];
++ processes.erase(pid);
++ delete process;
++ }
++ }
++
++private:
++ std::map<UPID, const ProcessBase*> processes;
++};
++
++
++extern PID<GarbageCollector> gc;
++
++} // namespace process {
++
++#endif // __PROCESS_GC_HPP__
+diff --git a/include/mesos/process/gmock.hpp b/include/mesos/process/gmock.hpp
+new file mode 100644
+index 0000000..a8cab4c
+--- /dev/null
++++ b/include/mesos/process/gmock.hpp
+@@ -0,0 +1,327 @@
++#ifndef __PROCESS_GMOCK_HPP__
++#define __PROCESS_GMOCK_HPP__
++
++#include <pthread.h>
++
++#include <gmock/gmock.h>
++
++#include <tr1/tuple>
++
++#include <process/dispatch.hpp>
++#include <process/event.hpp>
++#include <process/filter.hpp>
++#include <process/pid.hpp>
++
++#include <stout/exit.hpp>
++#include <stout/nothing.hpp>
++
++
++// THIS IS DEPRECATED AND BROKEN! REPLACE ALL USES!
++#define EXPECT_MESSAGE(name, from, to) \
++ EXPECT_CALL(*new process::MockFilter(), \
++ filter(testing::A<const process::MessageEvent&>())) \
++ .With(process::MessageMatcher(name, from, to))
++
++
++// THIS IS DEPRECATED AND BROKEN! REPLACE ALL USES!
++#define EXPECT_DISPATCH(pid, method) \
++ EXPECT_CALL(*new process::MockFilter(), \
++ filter(testing::A<const process::DispatchEvent&>())) \
++ .With(process::DispatchMatcher(pid, method))
++
++
++#define FUTURE_MESSAGE(name, from, to) \
++ process::FutureMessage(name, from, to)
++
++#define DROP_MESSAGE(name, from, to) \
++ process::FutureMessage(name, from, to, true)
++
++#define FUTURE_DISPATCH(pid, method) \
++ process::FutureDispatch(pid, method)
++
++#define DROP_DISPATCH(pid, method) \
++ process::FutureDispatch(pid, method, true)
++
++#define DROP_MESSAGES(name, from, to) \
++ process::DropMessages(name, from, to)
++
++#define DROP_DISPATCHES(pid, method) \
++ process::DropDispatches(pid, method)
++
++
++ACTION_TEMPLATE(PromiseArg,
++ HAS_1_TEMPLATE_PARAMS(int, k),
++ AND_1_VALUE_PARAMS(promise))
++{
++ // TODO(benh): Use a shared_ptr for promise to defend against this
++ // action getting invoked more than once (e.g., used via
++ // WillRepeatedly). We won't be able to set it a second time but at
++ // least we won't get a segmentation fault. We could also consider
++ // warning users if they attempted to set it more than once.
++ promise->set(std::tr1::get<k>(args));
++ delete promise;
++}
++
++
++template <int index, typename T>
++PromiseArgActionP<index, process::Promise<T>*> FutureArg(
++ process::Future<T>* future)
++{
++ process::Promise<T>* promise = new process::Promise<T>();
++ *future = promise->future();
++ return PromiseArg<index>(promise);
++}
++
++
++ACTION_TEMPLATE(PromiseArgField,
++ HAS_1_TEMPLATE_PARAMS(int, k),
++ AND_2_VALUE_PARAMS(field, promise))
++{
++ // TODO(benh): Use a shared_ptr for promise to defend against this
++ // action getting invoked more than once (e.g., used via
++ // WillRepeatedly). We won't be able to set it a second time but at
++ // least we won't get a segmentation fault. We could also consider
++ // warning users if they attempted to set it more than once.
++ promise->set(*(std::tr1::get<k>(args).*field));
++ delete promise;
++}
++
++
++template <int index, typename Field, typename T>
++PromiseArgFieldActionP2<index, Field, process::Promise<T>*> FutureArgField(
++ Field field,
++ process::Future<T>* future)
++{
++ process::Promise<T>* promise = new process::Promise<T>();
++ *future = promise->future();
++ return PromiseArgField<index>(field, promise);
++}
++
++
++ACTION_P2(PromiseSatisfy, promise, value)
++{
++ promise->set(value);
++ delete promise;
++}
++
++
++template <typename T>
++PromiseSatisfyActionP2<process::Promise<T>*, T> FutureSatisfy(
++ process::Future<T>* future,
++ T t)
++{
++ process::Promise<T>* promise = new process::Promise<T>();
++ *future = promise->future();
++ return PromiseSatisfy(promise, t);
++}
++
++
++inline PromiseSatisfyActionP2<process::Promise<Nothing>*, Nothing>
++FutureSatisfy(process::Future<Nothing>* future)
++{
++ process::Promise<Nothing>* promise = new process::Promise<Nothing>();
++ *future = promise->future();
++ return PromiseSatisfy(promise, Nothing());
++}
++
++
++namespace process {
++
++class MockFilter : public Filter
++{
++public:
++ MockFilter()
++ {
++ EXPECT_CALL(*this, filter(testing::A<const MessageEvent&>()))
++ .WillRepeatedly(testing::Return(false));
++ EXPECT_CALL(*this, filter(testing::A<const DispatchEvent&>()))
++ .WillRepeatedly(testing::Return(false));
++ EXPECT_CALL(*this, filter(testing::A<const HttpEvent&>()))
++ .WillRepeatedly(testing::Return(false));
++ EXPECT_CALL(*this, filter(testing::A<const ExitedEvent&>()))
++ .WillRepeatedly(testing::Return(false));
++ }
++
++ MOCK_METHOD1(filter, bool(const MessageEvent&));
++ MOCK_METHOD1(filter, bool(const DispatchEvent&));
++ MOCK_METHOD1(filter, bool(const HttpEvent&));
++ MOCK_METHOD1(filter, bool(const ExitedEvent&));
++};
++
++
++// A definition of a libprocess filter to enable waiting for events
++// (such as messages or dispatches) via in tests. This is not meant to
++// be used directly by tests; tests should use macros like
++// FUTURE_MESSAGE and FUTURE_DISPATCH instead.
++class TestsFilter : public Filter
++{
++public:
++ TestsFilter()
++ {
++ // We use a recursive mutex here in the event that satisfying the
++ // future created in FutureMessage or FutureDispatch via the
++ // FutureArgField or FutureSatisfy actions invokes callbacks (from
++ // Future::then or Future::onAny, etc) that themselves invoke
++ // FutureDispatch or FutureMessage.
++ pthread_mutexattr_t attr;
++ pthread_mutexattr_init(&attr);
++ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
++ pthread_mutex_init(&mutex, &attr);
++ pthread_mutexattr_destroy(&attr);
++ }
++
++ virtual bool filter(const MessageEvent& event) { return handle(event); }
++ virtual bool filter(const DispatchEvent& event) { return handle(event); }
++ virtual bool filter(const HttpEvent& event) { return handle(event); }
++ virtual bool filter(const ExitedEvent& event) { return handle(event); }
++
++ template <typename T>
++ bool handle(const T& t)
++ {
++ pthread_mutex_lock(&mutex);
++ bool drop = mock.filter(t);
++ pthread_mutex_unlock(&mutex);
++ return drop;
++ }
++
++ MockFilter mock;
++ pthread_mutex_t mutex;;
++};
++
++
++class FilterTestEventListener : public ::testing::EmptyTestEventListener
++{
++public:
++ // Returns the singleton instance of the listener.
++ static FilterTestEventListener* instance()
++ {
++ static FilterTestEventListener* listener = new FilterTestEventListener();
++ return listener;
++ }
++
++ // Installs and returns the filter, creating it if necessary.
++ TestsFilter* install()
++ {
++ if (!started) {
++ EXIT(1)
++ << "To use FUTURE/DROP_MESSAGE/DISPATCH, etc. you need to do the "
++ << "following before you invoke RUN_ALL_TESTS():\n\n"
++ << "\t::testing::TestEventListeners& listeners =\n"
++ << "\t ::testing::UnitTest::GetInstance()->listeners();\n"
++ << "\tlisteners.Append(process::FilterTestEventListener::instance());";
++ }
++
++ if (filter != NULL) {
++ return filter;
++ }
++
++ filter = new TestsFilter();
++
++ // Set the filter in libprocess.
++ process::filter(filter);
++
++ return filter;
++ }
++
++ virtual void OnTestProgramStart(const ::testing::UnitTest&)
++ {
++ started = true;
++ }
++
++ virtual void OnTestEnd(const ::testing::TestInfo&)
++ {
++ if (filter != NULL) {
++ // Remove the filter in libprocess _before_ deleting.
++ process::filter(NULL);
++ delete filter;
++ filter = NULL;
++ }
++ }
++
++private:
++ FilterTestEventListener() : filter(NULL), started(false) {}
++
++ TestsFilter* filter;
++
++ // Indicates if we got the OnTestProgramStart callback in order to
++ // detect if we have been properly added as a listener.
++ bool started;
++};
++
++
++MATCHER_P3(MessageMatcher, name, from, to, "")
++{
++ const MessageEvent& event = ::std::tr1::get<0>(arg);
++ return (testing::Matcher<std::string>(name).Matches(event.message->name) &&
++ testing::Matcher<UPID>(from).Matches(event.message->from) &&
++ testing::Matcher<UPID>(to).Matches(event.message->to));
++}
++
++
++MATCHER_P2(DispatchMatcher, pid, method, "")
++{
++ const DispatchEvent& event = ::std::tr1::get<0>(arg);
++ return (testing::Matcher<UPID>(pid).Matches(event.pid) &&
++ testing::Matcher<std::string>(internal::canonicalize(method))
++ .Matches(event.method));
++}
++
++
++template <typename Name, typename From, typename To>
++Future<Message> FutureMessage(Name name, From from, To to, bool drop = false)
++{
++ TestsFilter* filter = FilterTestEventListener::instance()->install();
++ pthread_mutex_lock(&filter->mutex);
++ Future<Message> future;
++ EXPECT_CALL(filter->mock, filter(testing::A<const MessageEvent&>()))
++ .With(MessageMatcher(name, from, to))
++ .WillOnce(testing::DoAll(FutureArgField<0>(&MessageEvent::message, &future),
++ testing::Return(drop)))
++ .RetiresOnSaturation(); // Don't impose any subsequent expectations.
++ pthread_mutex_unlock(&filter->mutex);
++ return future;
++}
++
++
++template <typename PID, typename Method>
++Future<Nothing> FutureDispatch(PID pid, Method method, bool drop = false)
++{
++ TestsFilter* filter = FilterTestEventListener::instance()->install();
++ pthread_mutex_lock(&filter->mutex);
++ Future<Nothing> future;
++ EXPECT_CALL(filter->mock, filter(testing::A<const DispatchEvent&>()))
++ .With(DispatchMatcher(pid, method))
++ .WillOnce(testing::DoAll(FutureSatisfy(&future),
++ testing::Return(drop)))
++ .RetiresOnSaturation(); // Don't impose any subsequent expectations.
++ pthread_mutex_unlock(&filter->mutex);
++ return future;
++}
++
++
++template <typename Name, typename From, typename To>
++void DropMessages(Name name, From from, To to)
++{
++ TestsFilter* filter = FilterTestEventListener::instance()->install();
++ pthread_mutex_lock(&filter->mutex);
++ EXPECT_CALL(filter->mock, filter(testing::A<const MessageEvent&>()))
++ .With(MessageMatcher(name, from, to))
++ .WillRepeatedly(testing::Return(true));
++ pthread_mutex_unlock(&filter->mutex);
++}
++
++
++template <typename PID, typename Method>
++void DropDispatches(PID pid, Method method)
++{
++ TestsFilter* filter = FilterTestEventListener::instance()->install();
++ pthread_mutex_lock(&filter->mutex);
++ EXPECT_CALL(filter->mock, filter(testing::A<const DispatchEvent&>()))
++ .With(DispatchMatcher(pid, method))
++ .WillRepeatedly(testing::Return(true));
++ pthread_mutex_unlock(&filter->mutex);
++}
++
++} // namespace process {
++
++#endif // __PROCESS_GMOCK_HPP__
+diff --git a/include/mesos/process/gtest.hpp b/include/mesos/process/gtest.hpp
+new file mode 100644
+index 0000000..753ef74
+--- /dev/null
++++ b/include/mesos/process/gtest.hpp
+@@ -0,0 +1,338 @@
++#ifndef __PROCESS_GTEST_HPP__
++#define __PROCESS_GTEST_HPP__
++
++#include <gtest/gtest.h>
++
++#include <string>
++
++#include <process/clock.hpp>
++#include <process/future.hpp>
++#include <process/http.hpp>
++
++#include <stout/duration.hpp>
++#include <stout/option.hpp>
++
++namespace process {
++
++// A simple test event listener that makes sure to resume the clock
++// after each test even if the previous test had a partial result
++// (i.e., an ASSERT_* failed).
++class ClockTestEventListener : public ::testing::EmptyTestEventListener
++{
++public:
++ // Returns the singleton instance of the listener.
++ static ClockTestEventListener* instance()
++ {
++ static ClockTestEventListener* listener = new ClockTestEventListener();
++ return listener;
++ }
++
++ virtual void OnTestEnd(const ::testing::TestInfo&)
++ {
++ if (process::Clock::paused()) {
++ process::Clock::resume();
++ }
++ }
++private:
++ ClockTestEventListener() {}
++};
++
++} // namespace process {
++
++template <typename T>
++::testing::AssertionResult AwaitAssertReady(
++ const char* expr,
++ const char*, // Unused string representation of 'duration'.
++ const process::Future<T>& actual,
++ const Duration& duration)
++{
++ if (!actual.await(duration)) {
++ return ::testing::AssertionFailure()
++ << "Failed to wait " << duration << " for " << expr;
++ } else if (actual.isDiscarded()) {
++ return ::testing::AssertionFailure()
++ << expr << " was discarded";
++ } else if (actual.isFailed()) {
++ return ::testing::AssertionFailure()
++ << "(" << expr << ").failure(): " << actual.failure();
++ }
++
++ return ::testing::AssertionSuccess();
++}
++
++
++template <typename T>
++::testing::AssertionResult AwaitAssertFailed(
++ const char* expr,
++ const char*, // Unused string representation of 'duration'.
++ const process::Future<T>& actual,
++ const Duration& duration)
++{
++ if (!actual.await(duration)) {
++ return ::testing::AssertionFailure()
++ << "Failed to wait " << duration << " for " << expr;
++ } else if (actual.isDiscarded()) {
++ return ::testing::AssertionFailure()
++ << expr << " was discarded";
++ } else if (actual.isReady()) {
++ return ::testing::AssertionFailure()
++ << expr << " is ready (" << ::testing::PrintToString(actual.get()) << ")";
++ }
++
++ return ::testing::AssertionSuccess();
++}
++
++
++template <typename T>
++::testing::AssertionResult AwaitAssertDiscarded(
++ const char* expr,
++ const char*, // Unused string representation of 'duration'.
++ const process::Future<T>& actual,
++ const Duration& duration)
++{
++ if (!actual.await(duration)) {
++ return ::testing::AssertionFailure()
++ << "Failed to wait " << duration << " for " << expr;
++ } else if (actual.isFailed()) {
++ return ::testing::AssertionFailure()
++ << "(" << expr << ").failure(): " << actual.failure();
++ } else if (actual.isReady()) {
++ return ::testing::AssertionFailure()
++ << expr << " is ready (" << ::testing::PrintToString(actual.get()) << ")";
++ }
++
++ return ::testing::AssertionSuccess();
++}
++
++
++template <typename T1, typename T2>
++::testing::AssertionResult AwaitAssertEq(
++ const char* expectedExpr,
++ const char* actualExpr,
++ const char* durationExpr,
++ const T1& expected,
++ const process::Future<T2>& actual,
++ const Duration& duration)
++{
++ const ::testing::AssertionResult result =
++ AwaitAssertReady(actualExpr, durationExpr, actual, duration);
++
++ if (result) {
++ if (expected == actual.get()) {
++ return ::testing::AssertionSuccess();
++ } else {
++ return ::testing::AssertionFailure()
++ << "Value of: (" << actualExpr << ").get()\n"
++ << " Actual: " << ::testing::PrintToString(actual.get()) << "\n"
++ << "Expected: " << expectedExpr << "\n"
++ << "Which is: " << ::testing::PrintToString(expected);
++ }
++ }
++
++ return result;
++}
++
++
++#define AWAIT_ASSERT_READY_FOR(actual, duration) \
++ ASSERT_PRED_FORMAT2(AwaitAssertReady, actual, duration)
++
++
++#define AWAIT_ASSERT_READY(actual) \
++ AWAIT_ASSERT_READY_FOR(actual, Seconds(10))
++
++
++#define AWAIT_READY_FOR(actual, duration) \
++ AWAIT_ASSERT_READY_FOR(actual, duration)
++
++
++#define AWAIT_READY(actual) \
++ AWAIT_ASSERT_READY(actual)
++
++
++#define AWAIT_EXPECT_READY_FOR(actual, duration) \
++ EXPECT_PRED_FORMAT2(AwaitAssertReady, actual, duration)
++
++
++#define AWAIT_EXPECT_READY(actual) \
++ AWAIT_EXPECT_READY_FOR(actual, Seconds(10))
++
++
++#define AWAIT_ASSERT_FAILED_FOR(actual, duration) \
++ ASSERT_PRED_FORMAT2(AwaitAssertFailed, actual, duration)
++
++
++#define AWAIT_ASSERT_FAILED(actual) \
++ AWAIT_ASSERT_FAILED_FOR(actual, Seconds(10))
++
++
++#define AWAIT_FAILED_FOR(actual, duration) \
++ AWAIT_ASSERT_FAILED_FOR(actual, duration)
++
++
++#define AWAIT_FAILED(actual) \
++ AWAIT_ASSERT_FAILED(actual)
++
++
++#define AWAIT_EXPECT_FAILED_FOR(actual, duration) \
++ EXPECT_PRED_FORMAT2(AwaitAssertFailed, actual, duration)
++
++
++#define AWAIT_EXPECT_FAILED(actual) \
++ AWAIT_EXPECT_FAILED_FOR(actual, Seconds(10))
++
++
++#define AWAIT_ASSERT_DISCARDED_FOR(actual, duration) \
++ ASSERT_PRED_FORMAT2(AwaitAssertDiscarded, actual, duration)
++
++
++#define AWAIT_ASSERT_DISCARDED(actual) \
++ AWAIT_ASSERT_DISCARDED_FOR(actual, Seconds(10))
++
++
++#define AWAIT_DISCARDED_FOR(actual, duration) \
++ AWAIT_ASSERT_DISCARDED_FOR(actual, duration)
++
++
++#define AWAIT_DISCARDED(actual) \
++ AWAIT_ASSERT_DISCARDED(actual)
++
++
++#define AWAIT_EXPECT_DISCARDED_FOR(actual, duration) \
++ EXPECT_PRED_FORMAT2(AwaitAssertDiscarded, actual, duration)
++
++
++#define AWAIT_EXPECT_DISCARDED(actual) \
++ AWAIT_EXPECT_DISCARDED_FOR(actual, Seconds(10))
++
++
++#define AWAIT_ASSERT_EQ_FOR(expected, actual, duration) \
++ ASSERT_PRED_FORMAT3(AwaitAssertEq, expected, actual, duration)
++
++
++#define AWAIT_ASSERT_EQ(expected, actual) \
++ AWAIT_ASSERT_EQ_FOR(expected, actual, Seconds(10))
++
++
++#define AWAIT_EQ(expected, actual) \
++ AWAIT_ASSERT_EQ(expected, actual)
++
++
++#define AWAIT_EXPECT_EQ_FOR(expected, actual, duration) \
++ EXPECT_PRED_FORMAT3(AwaitAssertEq, expected, actual, duration)
++
++
++#define AWAIT_EXPECT_EQ(expected, actual) \
++ AWAIT_EXPECT_EQ_FOR(expected, actual, Seconds(10))
++
++
++inline ::testing::AssertionResult AwaitAssertResponseStatusEq(
++ const char* expectedExpr,
++ const char* actualExpr,
++ const char* durationExpr,
++ const std::string& expected,
++ const process::Future<process::http::Response>& actual,
++ const Duration& duration)
++{
++ const ::testing::AssertionResult result =
++ AwaitAssertReady(actualExpr, durationExpr, actual, duration);
++
++ if (result) {
++ if (expected == actual.get().status) {
++ return ::testing::AssertionSuccess();
++ } else {
++ return ::testing::AssertionFailure()
++ << "Value of: (" << actualExpr << ").get().status\n"
++ << " Actual: " << ::testing::PrintToString(actual.get().status) << "\n"
++ << "Expected: " << expectedExpr << "\n"
++ << "Which is: " << ::testing::PrintToString(expected);
++ }
++ }
++
++ return result;
++}
++
++
++#define AWAIT_EXPECT_RESPONSE_STATUS_EQ_FOR(expected, actual, duration) \
++ EXPECT_PRED_FORMAT3(AwaitAssertResponseStatusEq, expected, actual, duration)
++
++
++#define AWAIT_EXPECT_RESPONSE_STATUS_EQ(expected, actual) \
++ AWAIT_EXPECT_RESPONSE_STATUS_EQ_FOR(expected, actual, Seconds(10))
++
++
++inline ::testing::AssertionResult AwaitAssertResponseBodyEq(
++ const char* expectedExpr,
++ const char* actualExpr,
++ const char* durationExpr,
++ const std::string& expected,
++ const process::Future<process::http::Response>& actual,
++ const Duration& duration)
++{
++ const ::testing::AssertionResult result =
++ AwaitAssertReady(actualExpr, durationExpr, actual, duration);
++
++ if (result) {
++ if (expected == actual.get().body) {
++ return ::testing::AssertionSuccess();
++ } else {
++ return ::testing::AssertionFailure()
++ << "Value of: (" << actualExpr << ").get().body\n"
++ << " Actual: " << ::testing::PrintToString(actual.get().body) << "\n"
++ << "Expected: " << expectedExpr << "\n"
++ << "Which is: " << ::testing::PrintToString(expected);
++ }
++ }
++
++ return result;
++}
++
++
++#define AWAIT_EXPECT_RESPONSE_BODY_EQ_FOR(expected, actual, duration) \
++ EXPECT_PRED_FORMAT3(AwaitAssertResponseBodyEq, expected, actual, duration)
++
++
++#define AWAIT_EXPECT_RESPONSE_BODY_EQ(expected, actual) \
++ AWAIT_EXPECT_RESPONSE_BODY_EQ_FOR(expected, actual, Seconds(10))
++
++
++inline ::testing::AssertionResult AwaitAssertResponseHeaderEq(
++ const char* expectedExpr,
++ const char* keyExpr,
++ const char* actualExpr,
++ const char* durationExpr,
++ const std::string& expected,
++ const std::string& key,
++ const process::Future<process::http::Response>& actual,
++ const Duration& duration)
++{
++ const ::testing::AssertionResult result =
++ AwaitAssertReady(actualExpr, durationExpr, actual, duration);
++
++ if (result) {
++ const Option<std::string> value = actual.get().headers.get(key);
++ if (value.isNone()) {
++ return ::testing::AssertionFailure()
++ << "Response does not contain header '" << key << "'";
++ } else if (expected == value.get()) {
++ return ::testing::AssertionSuccess();
++ } else {
++ return ::testing::AssertionFailure()
++ << "Value of: (" << actualExpr << ").get().headers[" << keyExpr << "]\n"
++ << " Actual: " << ::testing::PrintToString(value.get()) << "\n"
++ << "Expected: " << expectedExpr << "\n"
++ << "Which is: " << ::testing::PrintToString(expected);
++ }
++ }
++
++ return result;
++}
++
++
++#define AWAIT_EXPECT_RESPONSE_HEADER_EQ_FOR(expected, key, actual, duration) \
++ EXPECT_PRED_FORMAT4(AwaitAssertResponseHeaderEq, expected, key, actual, duration)
++
++
++#define AWAIT_EXPECT_RESPONSE_HEADER_EQ(expected, key, actual) \
++ AWAIT_EXPECT_RESPONSE_HEADER_EQ_FOR(expected, key, actual, Seconds(10))
++
++#endif // __PROCESS_GTEST_HPP__
+diff --git a/include/mesos/process/help.hpp b/include/mesos/process/help.hpp
+new file mode 100644
+index 0000000..7876a34
+--- /dev/null
++++ b/include/mesos/process/help.hpp
+@@ -0,0 +1,278 @@
++#ifndef __PROCESS_HELP_HPP__
++#define __PROCESS_HELP_HPP__
++
++#include <map>
++#include <string>
++#include <vector>
++
++#include <process/future.hpp>
++#include <process/http.hpp>
++#include <process/process.hpp>
++
++#include <stout/foreach.hpp>
++#include <stout/json.hpp>
++#include <stout/option.hpp>
++#include <stout/preprocessor.hpp>
++#include <stout/stringify.hpp>
++#include <stout/strings.hpp>
++
++namespace process {
++
++// Constructs a Markdown based help "page" for a route with the
++// following template:
++//
++// ### TL;DR; ###
++// tldr
++//
++// ### USAGE ###
++// usage
++//
++// ### DESCRIPTION ###
++// description
++//
++// references
++//
++// See the 'TLDR', 'USAGE', 'DESCRIPTION', and 'REFERENCES' helpers
++// below to more easily construct your help pages.
++inline std::string HELP(
++ std::string tldr,
++ std::string usage,
++ std::string description,
++ const Option<std::string>& references = None())
++{
++ // Make sure 'tldr', 'usage', and 'description' end with a newline.
++ if (!strings::endsWith(tldr, "\n")) {
++ tldr += "\n";
++ }
++
++ if (!strings::endsWith(usage, "\n")) {
++ usage += "\n";
++ }
++
++ if (!strings::endsWith(description, "\n")) {
++ description += "\n";
++ }
++
++ // Construct the help string.
++ std::string help =
++ "### TL;DR; ###\n" +
++ tldr +
++ "\n" +
++ "### USAGE ###\n" +
++ usage +
++ "\n" +
++ "### DESCRIPTION ###\n" +
++ description;
++
++ if (references.isSome()) {
++ help += "\n";
++ help += references.get();
++ }
++
++ return help;
++}
++
++
++// Helper for single-line TL;DR; that adds a newline.
++inline std::string TLDR(const std::string& tldr)
++{
++ return tldr + "\n";
++}
++
++
++// Helper for single-line usage that puts it in a blockquote as code
++// and adds a newline.
++inline std::string USAGE(const std::string& usage)
++{
++ return "> " + usage + "\n";
++}
++
++
++// Helpers for adding newlines to each line of a multi-line
++// description or references.
++#define LINE_TEMPLATE(Z, N, DATA) + CAT(line, N) + "\n"
++#define TEMPLATE(Z, N, DATA) \
++ inline std::string DESCRIPTION( \
++ ENUM_PARAMS(N, const std::string& line)) \
++ { \
++ return \
++ "" \
++ REPEAT_FROM_TO(0, N, LINE_TEMPLATE, _); \
++ } \
++ \
++ \
++ inline std::string REFERENCES( \
++ ENUM_PARAMS(N, const std::string& line)) \
++ { \
++ return \
++ "" \
++ REPEAT_FROM_TO(0, N, LINE_TEMPLATE, _); \
++ }
++
++ REPEAT_FROM_TO(1, 201, TEMPLATE, _) // Lines 1 -> 200.
++#undef TEMPLATE
++#undef LINE_TEMPLATE
++
++
++// Help process for serving /help, /help/id, and /help/id/name (see
++// Help::help below for more information).
++class Help : public Process<Help>
++{
++public:
++ Help() : ProcessBase("help") {}
++
++ // Adds 'help' for the route 'name' of the process with the
++ // specified 'id' (i.e., 'http://ip:port/id/name'). It's expected
++ // that 'help' is written using Markdown. When serving help to a
++ // browser the Markdown will be rendered into HTML while a tool like
++ // 'curl' or 'http' will just be given the Markdown directly (thus
++ // making it easy to get help without opening a browser).
++ // NOTE: There is no need to dispatch this directly; this gets
++ // automagically dispatched by 'ProcessBase::route'.
++ void add(const std::string& id,
++ const std::string& name,
++ const Option<std::string>& help)
++ {
++ if (id != "help") { // TODO(benh): Enable help for help.
++ if (help.isSome()) {
++ helps[id][name] = help.get();
++ } else {
++ helps[id][name] = "## No help page for `/" + id + name + "`\n";
++ }
++ route("/" + id, "Help for " + id, &Help::help);
++ }
++ }
++
++protected:
++ virtual void initialize()
++ {
++ route("/", None(), &Help::help);
++ }
++
++private:
++ // Handles the following:
++ //
++ // (1) http://ip:port/help
++ // (2) http://ip:port/help/id
++ // (3) http://ip:port/help/id/name
++ //
++ // Where 'id' and 'name' are replaced with a process ID and route
++ // name respectively. (1) provides a "table of contents" for all
++ // available processes while (2) provides a "table of contents" for
++ // all endpoints associated with a particular process and (3)
++ // provides the help associated with a particular endpoint of a
++ // process.
++ Future<http::Response> help(const http::Request& request)
++ {
++ // Split the path by '/'.
++ std::vector<std::string> tokens = strings::tokenize(request.path, "/");
++
++ Option<std::string> id = None();
++ Option<std::string> name = None();
++
++ if (tokens.size() > 3) {
++ return http::BadRequest("Malformed URL, expecting '/help/id/name/'\n");
++ } else if (tokens.size() == 3) {
++ id = tokens[1];
++ name = tokens[2];
++ } else if (tokens.size() > 1) {
++ id = tokens[1];
++ }
++
++ std::string document;
++ std::string references;
++
++ if (id.isNone()) { // http://ip:port/help
++ document += "## HELP\n";
++ foreachkey (const std::string& id, helps) {
++ document += "> [/" + id + "][" + id + "]\n";
++ references += "[" + id + "]: /help/" + id + "\n";
++ }
++ } else if (name.isNone()) { // http://ip:port/help/id
++ if (helps.count(id.get()) == 0) {
++ return http::BadRequest(
++ "No help available for '/" + id.get() + "'.\n");
++ }
++
++ document += "## `/" + id.get() + "` ##\n";
++ foreachkey (const std::string& name, helps[id.get()]) {
++ const std::string& path = id.get() + name;
++ document += "> [/" + path + "][" + path + "]\n";
++ references += "[" + path + "]: /help/" + path + "\n";
++ }
++ } else { // http://ip:port/help/id/name
++ if (helps.count(id.get()) == 0) {
++ return http::BadRequest(
++ "No help available for '/" + id.get() + "'.\n");
++ } else if (helps[id.get()].count("/" + name.get()) == 0) {
++ return http::BadRequest(
++ "No help available for '/" + id.get() + "/" + name.get() + "'.\n");
++ }
++
++ document += helps[id.get()]["/" + name.get()];
++ }
++
++ // Final Markdown is 'document' followed by the 'references'.
++ std::string markdown = document + "\n" + references;
++
++ // Just send the Markdown if we aren't speaking to a browser. For
++ // now we only check for the 'curl' or 'http' utilities.
++ Option<std::string> agent = request.headers.get("User-Agent");
++
++ if (agent.isSome() &&
++ (strings::startsWith(agent.get(), "curl") ||
++ strings::startsWith(agent.get(), "HTTPie"))) {
++ http::Response response = http::OK(markdown);
++ response.headers["Content-Type"] = "text/x-markdown";
++ return response;
++ }
++
++ // Need to JSONify the markdown for embedding into JavaScript.
++ markdown = stringify(JSON::String(markdown));
++
++ // Provide some JavaScript to render the Markdown into some aesthetically
++ // pleasing HTML. ;)
++ return http::OK(
++ "<html>"
++ "<head>"
++ "<title>Help</title>"
++ "<script src=\"/static/js/marked.min.js\"></script>"
++ "<script>"
++ " function loaded() {"
++ " marked.setOptions({ breaks: true });"
++ " document.body.innerHTML = marked(" + markdown + ");"
++ " }"
++ "</script>"
++ "<style>"
++ "body {"
++ " font-family: Helvetica, arial, sans-serif;"
++ " font-size: 14px;"
++ " line-height: 1.6;"
++ " padding-top: 10px;"
++ " padding-bottom: 10px;"
++ " background-color: white;"
++ " padding: 30px;"
++ "}"
++ "blockquote {"
++ " border-left: 5px solid #dddddd;"
++ " padding: 0 10px;"
++ " color: #777777;"
++ " margin: 0 0 20px;"
++ "}"
++ "a {"
++ " color: #0088cc;"
++ " text-decoration: none;"
++ "}"
++ "</style>"
++ "</head>"
++ "<body onload=\"loaded()\">"
++ "</body>"
++ "</html>");
++ }
++
++ std::map<std::string, std::map<std::string, std::string> > helps;
++};
++
++} // namespace process {
++
++#endif // __PROCESS_HELP_HPP__
+diff --git a/include/mesos/process/http.hpp b/include/mesos/process/http.hpp
+new file mode 100644
+index 0000000..5bdd520
+--- /dev/null
++++ b/include/mesos/process/http.hpp
+@@ -0,0 +1,541 @@
++#ifndef __PROCESS_HTTP_HPP__
++#define __PROCESS_HTTP_HPP__
++
++#include <cctype>
++#include <cstdlib>
++#include <iomanip>
++#include <sstream>
++#include <string>
++
++#include <limits.h>
++
++#include <process/future.hpp>
++#include <process/pid.hpp>
++
++#include <stout/error.hpp>
++#include <stout/hashmap.hpp>
++#include <stout/json.hpp>
++#include <stout/none.hpp>
++#include <stout/option.hpp>
++#include <stout/stringify.hpp>
++#include <stout/strings.hpp>
++#include <stout/try.hpp>
++
++namespace process {
++namespace http {
++
++struct Request
++{
++ // TODO(benh): Add major/minor version.
++ // TODO(bmahler): Header names are not case sensitive! Either make these
++ // case-insensitive, or add a variable for each header in HTTP 1.0/1.1 (like
++ // we've done here with keepAlive).
++ // Tracked by: https://issues.apache.org/jira/browse/MESOS-328.
++ hashmap<std::string, std::string> headers;
++ std::string method;
++ std::string url; // path?query#fragment
++ std::string path;
++ std::string fragment;
++ hashmap<std::string, std::string> query;
++ std::string body;
++ bool keepAlive;
++
++ // Returns whether the encoding is considered acceptable in the request.
++ // TODO(bmahler): Consider this logic being in decoder.hpp, and having the
++ // Request contain a member variable for each popular HTTP 1.0/1.1 header.
++ bool accepts(const std::string& encoding) const
++ {
++ // See RFC 2616, section 14.3 for the details.
++ Option<std::string> accepted = headers.get("Accept-Encoding");
++
++ if (accepted.isNone()) {
++ return false;
++ }
++
++ // Remove spaces and tabs for easier parsing.
++ accepted = strings::remove(accepted.get(), " ");
++ accepted = strings::remove(accepted.get(), "\t");
++ accepted = strings::remove(accepted.get(), "\n");
++
++ // From RFC 2616:
++ // 1. If the content-coding is one of the content-codings listed in
++ // the Accept-Encoding field, then it is acceptable, unless it is
++ // accompanied by a qvalue of 0. (As defined in section 3.9, a
++ // qvalue of 0 means "not acceptable.")
++ // 2. The special "*" symbol in an Accept-Encoding field matches any
++ // available content-coding not explicitly listed in the header
++ // field.
++
++ // First we'll look for the encoding specified explicitly, then '*'.
++ std::vector<std::string> candidates;
++ candidates.push_back(encoding); // Rule 1.
++ candidates.push_back("*"); // Rule 2.
++
++ foreach (std::string& candidate, candidates) {
++ // Is the candidate one of the accepted encodings?
++ foreach (const std::string& _encoding,
++ strings::tokenize(accepted.get(), ",")) {
++ if (strings::startsWith(_encoding, candidate)) {
++ // Is there a 0 q value? Ex: 'gzip;q=0.0'.
++ const std::map<std::string, std::vector<std::string> >& values =
++ strings::pairs(_encoding, ";", "=");
++
++ // Look for { "q": ["0"] }.
++ if (values.count("q") == 0 || values.find("q")->second.size() != 1) {
++ // No q value, or malformed q value.
++ return true;
++ }
++
++ // Is the q value > 0?
++ Try<double> value = numify<double>(values.find("q")->second[0]);
++ return value.isSome() && value.get() > 0;
++ }
++ }
++ }
++
++ // NOTE: 3 and 4 are partially ignored since we can only provide gzip.
++ // 3. If multiple content-codings are acceptable, then the acceptable
++ // content-coding with the highest non-zero qvalue is preferred.
++ // 4. The "identity" content-coding is always acceptable, unless
++ // specifically refused because the Accept-Encoding field includes
++ // "identity;q=0", or because the field includes "*;q=0" and does
++ // not explicitly include the "identity" content-coding. If the
++ // Accept-Encoding field-value is empty, then only the "identity"
++ // encoding is acceptable.
++ return false;
++ }
++};
++
++
++struct Response
++{
++ Response()
++ : type(NONE)
++ {}
++
++ Response(const std::string& _body)
++ : type(BODY),
++ body(_body)
++ {
++ headers["Content-Length"] = stringify(body.size());
++ }
++
++ // TODO(benh): Add major/minor version.
++ std::string status;
++ hashmap<std::string, std::string> headers;
++
++ // Either provide a "body", an absolute "path" to a file, or a
++ // "pipe" for streaming a response. Distinguish between the cases
++ // using 'type' below.
++ //
++ // BODY: Uses 'body' as the body of the response. These may be
++ // encoded using gzip for efficiency, if 'Content-Encoding' is not
++ // already specified.
++ //
++ // PATH: Attempts to perform a 'sendfile' operation on the file
++ // found at 'path'.
++ //
++ // PIPE: Splices data from 'pipe' using 'Transfer-Encoding=chunked'.
++ // Note that the read end of the pipe will be closed by libprocess
++ // either after the write end has been closed or if the socket the
++ // data is being spliced to has been closed (i.e., nobody is
++ // listening any longer). This can cause writes to the pipe to
++ // generate a SIGPIPE (which will terminate your program unless you
++ // explicitly ignore them or handle them).
++ //
++ // In all cases (BODY, PATH, PIPE), you are expected to properly
++ // specify the 'Content-Type' header, but the 'Content-Length' and
++ // or 'Transfer-Encoding' headers will be filled in for you.
++ enum {
++ NONE,
++ BODY,
++ PATH,
++ PIPE
++ } type;
++
++ std::string body;
++ std::string path;
++ int pipe; // See comment above regarding the semantics for closing.
++};
++
++
++struct OK : Response
++{
++ OK()
++ {
++ status = "200 OK";
++ }
++
++ OK(const char* body) : Response(std::string(body))
++ {
++ status = "200 OK";
++ }
++
++ OK(const std::string& body) : Response(body)
++ {
++ status = "200 OK";
++ }
++
++ OK(const JSON::Value& value, const Option<std::string>& jsonp = None())
++ {
++ type = BODY;
++
++ status = "200 OK";
++
++ std::ostringstream out;
++
++ if (jsonp.isSome()) {
++ out << jsonp.get() << "(";
++ }
++
++ JSON::render(out, value);
++
++ if (jsonp.isSome()) {
++ out << ");";
++ headers["Content-Type"] = "text/javascript";
++ } else {
++ headers["Content-Type"] = "application/json";
++ }
++
++ headers["Content-Length"] = stringify(out.str().size());
++ body = out.str().data();
++ }
++};
++
++
++struct TemporaryRedirect : Response
++{
++ TemporaryRedirect(const std::string& url)
++ {
++ status = "307 Temporary Redirect";
++ headers["Location"] = url;
++ }
++};
++
++
++struct BadRequest : Response
++{
++ BadRequest()
++ {
++ status = "400 Bad Request";
++ }
++
++ BadRequest(const std::string& body)
++ : Response(body)
++ {
++ status = "400 Bad Request";
++ }
++};
++
++
++struct NotFound : Response
++{
++ NotFound()
++ {
++ status = "404 Not Found";
++ }
++
++ NotFound(const std::string& body) : Response(body)
++ {
++ status = "404 Not Found";
++ }
++};
++
++
++struct InternalServerError : Response
++{
++ InternalServerError()
++ {
++ status = "500 Internal Server Error";
++ }
++
++ InternalServerError(const std::string& body) : Response(body)
++ {
++ status = "500 Internal Server Error";
++ }
++};
++
++
++struct ServiceUnavailable : Response
++{
++ ServiceUnavailable()
++ {
++ status = "503 Service Unavailable";
++ }
++
++ ServiceUnavailable(const std::string& body) : Response(body)
++ {
++ status = "503 Service Unavailable";
++ }
++};
++
++
++namespace path {
++
++// Parses an HTTP path into a map given a pattern (TODO(benh): Make
++// the patterns be regular expressions). This returns an error if
++// 'pattern' doesn't match 'path'. For example:
++//
++// parse("/books/{isbn}/chapters/{chapter}",
++// "/books/0304827484/chapters/3")
++//
++// Would return a map with the following:
++// books: "books"
++// isbn: "0304827484"
++// chapters: "chapters"
++// chapter: "3"
++//
++// Another example:
++//
++// parse("/books/{isbn}/chapters/{chapter}",
++// "/books/0304827484")
++//
++// Would return a map with the following:
++// books: "books"
++// isbn: "0304827484"
++//
++// And another:
++//
++// parse("/books/{isbn}/chapters/{chapter}",
++// "/books/0304827484/chapters")
++//
++// Would return a map with the following:
++// books: "books"
++// isbn: "0304827484"
++// chapters: "chapters"
++inline Try<hashmap<std::string, std::string> > parse(
++ const std::string& pattern,
++ const std::string& path)
++{
++ // Split the pattern by '/' into keys.
++ std::vector<std::string> keys = strings::tokenize(pattern, "/");
++
++ // Split the path by '/' into segments.
++ std::vector<std::string> segments = strings::tokenize(path, "/");
++
++ hashmap<std::string, std::string> result;
++
++ while (!segments.empty()) {
++ if (keys.empty()) {
++ return Error(
++ "Not expecting suffix '" + strings::join("/", segments) + "'");
++ }
++
++ std::string key = keys.front();
++
++ if (strings::startsWith(key, "{") &&
++ strings::endsWith(key, "}")) {
++ key = strings::remove(key, "{", strings::PREFIX);
++ key = strings::remove(key, "}", strings::SUFFIX);
++ } else if (key != segments.front()) {
++ return Error("Expecting '" + key + "' not '" + segments.front() + "'");
++ }
++
++ result[key] = segments.front();
++
++ keys.erase(keys.begin());
++ segments.erase(segments.begin());
++ }
++
++ return result;
++}
++
++} // namespace path {
++
++
++namespace query {
++
++// Parses an HTTP query string into a map. For example:
++//
++// parse("foo=1;bar=2;baz;foo=3")
++//
++// Would return a map with the following:
++// bar: "2"
++// baz: ""
++// foo: "3"
++//
++// We use the last value for a key for simplicity, since the RFC does not
++// specify how to handle duplicate keys:
++// http://en.wikipedia.org/wiki/Query_string
++// TODO(bmahler): If needed, investigate populating the query map inline
++// for better performance.
++inline hashmap<std::string, std::string> parse(const std::string& query)
++{
++ hashmap<std::string, std::string> result;
++
++ const std::vector<std::string>& tokens = strings::tokenize(query, ";&");
++ foreach (const std::string& token, tokens) {
++ const std::vector<std::string>& pairs = strings::split(token, "=");
++ if (pairs.size() == 2) {
++ result[pairs[0]] = pairs[1];
++ } else if (pairs.size() == 1) {
++ result[pairs[0]] = "";
++ }
++ }
++
++ return result;
++}
++
++} // namespace query {
++
++
++// Returns a percent-encoded string according to RFC 3986.
++// The input string must not already be percent encoded.
++inline std::string encode(const std::string& s)
++{
++ std::ostringstream out;
++
++ foreach (unsigned char c, s) {
++ switch (c) {
++ // Reserved characters.
++ case '$':
++ case '&':
++ case '+':
++ case ',':
++ case '/':
++ case ':':
++ case ';':
++ case '=':
++ case '?':
++ case '@':
++ // Unsafe characters.
++ case ' ':
++ case '"':
++ case '<':
++ case '>':
++ case '#':
++ case '%':
++ case '{':
++ case '}':
++ case '|':
++ case '\\':
++ case '^':
++ case '~':
++ case '[':
++ case ']':
++ case '`':
++ // NOTE: The cast to unsigned int is needed.
++ out << '%' << std::setfill('0') << std::setw(2) << std::hex
++ << std::uppercase << (unsigned int) c;
++ break;
++ default:
++ // ASCII control characters and non-ASCII characters.
++ // NOTE: The cast to unsigned int is needed.
++ if (c < 0x20 || c > 0x7F) {
++ out << '%' << std::setfill('0') << std::setw(2) << std::hex
++ << std::uppercase << (unsigned int) c;
++ } else {
++ out << c;
++ }
++ break;
++ }
++ }
++
++ return out.str();
++}
++
++
++// Decodes a percent-encoded string according to RFC 3986.
++// The input string must not already be decoded.
++// Returns error on the occurrence of a malformed % escape in s.
++inline Try<std::string> decode(const std::string& s)
++{
++ std::ostringstream out;
++
++ for (size_t i = 0; i < s.length(); ++i) {
++ if (s[i] != '%') {
++ out << s[i];
++ continue;
++ }
++
++ // We now expect two more characters: % HEXDIG HEXDIG
++ if (i + 2 >= s.length() || !isxdigit(s[i+1]) || !isxdigit(s[i+2])) {
++ return Error(
++ "Malformed % escape in '" + s + "': '" + s.substr(i, 3) + "'");
++ }
++
++ // Convert from HEXDIG HEXDIG to char value.
++ std::istringstream in(s.substr(i + 1, 2));
++ unsigned long l;
++ in >> std::hex >> l;
++ if (l > UCHAR_MAX) {
++ std::cerr << "Unexpected conversion from hex string: "
++ << s.substr(i + 1, 2) << " to unsigned long: "
++ << l << std::endl;
++ abort();
++ }
++ out << static_cast<unsigned char>(l);
++
++ i += 2;
++ }
++
++ return out.str();
++}
++
++
++// Sends a blocking HTTP GET request to the process with the given upid.
++// Returns the HTTP response from the process, read asynchronously.
++//
++// TODO(bmahler): Have the request sent asynchronously as well.
++// TODO(bmahler): For efficiency, this should properly use the ResponseDecoder
++// on the read stream, rather than parsing the full string response at the end.
++Future<Response> get(
++ const UPID& upid,
++ const std::string& path = "",
++ const std::string& query = "");
++
++
++// Status code reason strings, from the HTTP1.1 RFC:
++// http://www.w3.org/Protocols/rfc2616/rfc2616-sec6.html
++extern hashmap<uint16_t, std::string> statuses;
++
++
++inline void initialize()
++{
++ statuses[100] = "100 Continue";
++ statuses[101] = "101 Switching Protocols";
++ statuses[200] = "200 OK";
++ statuses[201] = "201 Created";
++ statuses[202] = "202 Accepted";
++ statuses[203] = "203 Non-Authoritative Information";
++ statuses[204] = "204 No Content";
++ statuses[205] = "205 Reset Content";
++ statuses[206] = "206 Partial Content";
++ statuses[300] = "300 Multiple Choices";
++ statuses[301] = "301 Moved Permanently";
++ statuses[302] = "302 Found";
++ statuses[303] = "303 See Other";
++ statuses[304] = "304 Not Modified";
++ statuses[305] = "305 Use Proxy";
++ statuses[307] = "307 Temporary Redirect";
++ statuses[400] = "400 Bad Request";
++ statuses[401] = "401 Unauthorized";
++ statuses[402] = "402 Payment Required";
++ statuses[403] = "403 Forbidden";
++ statuses[404] = "404 Not Found";
++ statuses[405] = "405 Method Not Allowed";
++ statuses[406] = "406 Not Acceptable";
++ statuses[407] = "407 Proxy Authentication Required";
++ statuses[408] = "408 Request Time-out";
++ statuses[409] = "409 Conflict";
++ statuses[410] = "410 Gone";
++ statuses[411] = "411 Length Required";
++ statuses[412] = "412 Precondition Failed";
++ statuses[413] = "413 Request Entity Too Large";
++ statuses[414] = "414 Request-URI Too Large";
++ statuses[415] = "415 Unsupported Media Type";
++ statuses[416] = "416 Requested range not satisfiable";
++ statuses[417] = "417 Expectation Failed";
++ statuses[500] = "500 Internal Server Error";
++ statuses[501] = "501 Not Implemented";
++ statuses[502] = "502 Bad Gateway";
++ statuses[503] = "503 Service Unavailable";
++ statuses[504] = "504 Gateway Time-out";
++ statuses[505] = "505 HTTP Version not supported";
++}
++
++
++} // namespace http {
++} // namespace process {
++
++#endif // __PROCESS_HTTP_HPP__
+diff --git a/include/mesos/process/id.hpp b/include/mesos/process/id.hpp
+new file mode 100644
+index 0000000..8c256b9
+--- /dev/null
++++ b/include/mesos/process/id.hpp
+@@ -0,0 +1,16 @@
++#ifndef __PROCESS_ID_HPP__
++#define __PROCESS_ID_HPP__
++
++#include <string>
++
++namespace process {
++namespace ID {
++
++// Returns 'prefix(N)' where N represents the number of instances
++// where this prefix has been used to generate an ID.
++std::string generate(const std::string& prefix = "");
++
++} // namespace ID {
++} // namespace process {
++
++#endif // __PROCESS_ID_HPP__
+diff --git a/include/mesos/process/io.hpp b/include/mesos/process/io.hpp
+new file mode 100644
+index 0000000..8cf3244
+--- /dev/null
++++ b/include/mesos/process/io.hpp
+@@ -0,0 +1,44 @@
++#ifndef __PROCESS_IO_HPP__
++#define __PROCESS_IO_HPP__
++
++#include <cstring> // For size_t.
++#include <string>
++
++#include <process/future.hpp>
++
++namespace process {
++namespace io {
++
++// Possible events for polling.
++const short READ = 0x01;
++const short WRITE = 0x02;
++
++// Buffered read chunk size. Roughly 16 pages.
++const size_t BUFFERED_READ_SIZE = 16*4096;
++
++// TODO(benh): Add a version which takes multiple file descriptors.
++// Returns the events (a subset of the events specified) that can be
++// performed on the specified file descriptor without blocking.
++Future<short> poll(int fd, short events);
++
++
++// Performs a single non-blocking read by polling on the specified file
++// descriptor until any data can be be read. The future will become ready when
++// some data is read (may be less than that specified by size). A future failure
++// will be returned if an error is detected. If end-of-file is reached, value
++// zero will be returned. Note that the return type of this function differs
++// from the standard 'read'. In particular, this function returns the number of
++// bytes read or zero on end-of-file (an error is indicated by failing the
++// future, thus only a 'size_t' is necessary rather than a 'ssize_t').
++Future<size_t> read(int fd, void* data, size_t size);
++
++
++// Performs a series of asynchronous reads, until EOF is reached.
++// NOTE: When using this, ensure the sender will close the connection
++// so that EOF can be reached.
++Future<std::string> read(int fd);
++
++} // namespace io {
++} // namespace process {
++
++#endif // __PROCESS_IO_HPP__
+diff --git a/include/mesos/process/latch.hpp b/include/mesos/process/latch.hpp
+new file mode 100644
+index 0000000..5170aa8
+--- /dev/null
++++ b/include/mesos/process/latch.hpp
+@@ -0,0 +1,33 @@
++#ifndef __PROCESS_LATCH_HPP__
++#define __PROCESS_LATCH_HPP__
++
++#include <process/pid.hpp>
++
++#include <stout/duration.hpp>
++
++namespace process {
++
++class Latch
++{
++public:
++ Latch();
++ virtual ~Latch();
++
++ bool operator == (const Latch& that) const { return pid == that.pid; }
++ bool operator < (const Latch& that) const { return pid < that.pid; }
++
++ void trigger();
++ bool await(const Duration& duration = Seconds(-1));
++
++private:
++ // Not copyable, not assignable.
++ Latch(const Latch& that);
++ Latch& operator = (const Latch& that);
++
++ bool triggered;
++ UPID pid;
++};
++
++} // namespace process {
++
++#endif // __PROCESS_LATCH_HPP__
+diff --git a/include/mesos/process/limiter.hpp b/include/mesos/process/limiter.hpp
+new file mode 100644
+index 0000000..bbe8226
+--- /dev/null
++++ b/include/mesos/process/limiter.hpp
+@@ -0,0 +1,140 @@
++#ifndef __PROCESS_LIMITER_HPP__
++#define __PROCESS_LIMITER_HPP__
++
++#include <deque>
++
++#include <process/delay.hpp>
++#include <process/dispatch.hpp>
++#include <process/future.hpp>
++#include <process/process.hpp>
++#include <process/timeout.hpp>
++
++#include <stout/duration.hpp>
++#include <stout/foreach.hpp>
++#include <stout/nothing.hpp>
++
++namespace process {
++
++// Forward declaration.
++class RateLimiterProcess;
++
++// Provides an abstraction that rate limits the number of "permits"
++// that can be acquired over some duration.
++// NOTE: Currently, each libprocess Process should use a separate
++// RateLimiter instance. This is because if multiple processes share
++// a RateLimiter instance, by the time a process acts on the Future
++// returned by 'acquire()' another process might have acquired the
++// next permit and do its rate limited operation.
++class RateLimiter
++{
++public:
++ RateLimiter(int permits, const Duration& duration);
++ ~RateLimiter();
++
++ // Returns a future that becomes ready when the permit is acquired.
++ Future<Nothing> acquire();
++
++private:
++ // Not copyable, not assignable.
++ RateLimiter(const RateLimiter&);
++ RateLimiter& operator = (const RateLimiter&);
++
++ RateLimiterProcess* process;
++};
++
++
++class RateLimiterProcess : public Process<RateLimiterProcess>
++{
++public:
++ RateLimiterProcess(int _permits, const Duration& _duration)
++ : permits(_permits), duration(_duration)
++ {
++ CHECK_GT(permits, 0);
++ CHECK_GT(duration.secs(), 0);
++ }
++
++ virtual void finalize()
++ {
++ foreach (Promise<Nothing>* promise, promises) {
++ promise->future().discard();
++ delete promise;
++ }
++ promises.clear();
++ }
++
++ Future<Nothing> acquire()
++ {
++ if (!promises.empty()) {
++ // Need to wait for others to get permits first.
++ Promise<Nothing>* promise = new Promise<Nothing>();
++ promises.push_back(promise);
++ return promise->future();
++ } if (timeout.remaining() > Seconds(0)) {
++ // Need to wait a bit longer, but first one in the queue.
++ Promise<Nothing>* promise = new Promise<Nothing>();
++ promises.push_back(promise);
++ delay(timeout.remaining(), self(), &Self::_acquire);
++ return promise->future();
++ }
++
++ // No need to wait!
++ double rate = permits / duration.secs();
++ timeout = Seconds(1) / rate;
++ return Nothing();
++ }
++
++private:
++ // Not copyable, not assignable.
++ RateLimiterProcess(const RateLimiterProcess&);
++ RateLimiterProcess& operator = (const RateLimiterProcess&);
++
++ void _acquire()
++ {
++ CHECK(!promises.empty());
++
++ Promise<Nothing>* promise = promises.front();
++ promises.pop_front();
++
++ promise->set(Nothing());
++
++ double rate = permits / duration.secs();
++ timeout = Seconds(1) / rate;
++
++ // Repeat if necessary.
++ if (!promises.empty()) {
++ delay(timeout.remaining(), self(), &Self::_acquire);
++ }
++ }
++
++ const int permits;
++ const Duration duration;
++
++ Timeout timeout;
++
++ std::deque<Promise<Nothing>*> promises;
++};
++
++
++inline RateLimiter::RateLimiter(int permits, const Duration& duration)
++{
++ process = new RateLimiterProcess(permits, duration);
++ spawn(process);
++}
++
++
++inline RateLimiter::~RateLimiter()
++{
++ terminate(process);
++ wait(process);
++ delete process;
++}
++
++
++inline Future<Nothing> RateLimiter::acquire()
++{
++ return dispatch(process, &RateLimiterProcess::acquire);
++}
++
++} // namespace process {
++
++#endif // __PROCESS_LIMITER_HPP__
+diff --git a/include/mesos/process/logging.hpp b/include/mesos/process/logging.hpp
+new file mode 100644
+index 0000000..f4fb619
+--- /dev/null
++++ b/include/mesos/process/logging.hpp
+@@ -0,0 +1,113 @@
++#ifndef __PROCESS_LOGGING_HPP__
++#define __PROCESS_LOGGING_HPP__
++
++#include <glog/logging.h>
++
++#include <process/delay.hpp>
++#include <process/future.hpp>
++#include <process/http.hpp>
++#include <process/process.hpp>
++#include <process/timeout.hpp>
++
++#include <stout/duration.hpp>
++#include <stout/numify.hpp>
++#include <stout/option.hpp>
++#include <stout/stringify.hpp>
++#include <stout/try.hpp>
++
++namespace process {
++
++class Logging : public Process<Logging>
++{
++public:
++ Logging()
++ : ProcessBase("logging"),
++ original(FLAGS_v)
++ {
++ // Make sure all reads/writes can be done atomically (i.e., to
++ // make sure VLOG(*) statements don't read partial writes).
++ // TODO(benh): Use "atomics" primitives for doing reads/writes of
++ // FLAGS_v anyway to account for proper memory barriers.
++ CHECK(sizeof(FLAGS_v) == sizeof(int32_t));
++ }
++
++ virtual ~Logging() {}
++
++protected:
++ virtual void initialize()
++ {
++ route("/toggle", TOGGLE_HELP, &This::toggle);
++ }
++
++private:
++ Future<http::Response> toggle(const http::Request& request)
++ {
++ Option<std::string> level = request.query.get("level");
++ Option<std::string> duration = request.query.get("duration");
++
++ if (level.isNone() && duration.isNone()) {
++ return http::OK(stringify(FLAGS_v) + "\n");
++ }
++
++ if (level.isSome() && duration.isNone()) {
++ return http::BadRequest("Expecting 'duration=value' in query.\n");
++ } else if (level.isNone() && duration.isSome()) {
++ return http::BadRequest("Expecting 'level=value' in query.\n");
++ }
++
++ Try<int> v = numify<int>(level.get());
++
++ if (v.isError()) {
++ return http::BadRequest(v.error() + ".\n");
++ }
++
++ if (v.get() < 0) {
++ return http::BadRequest("Invalid level '" + stringify(v.get()) + "'.\n");
++ } else if (v.get() < original) {
++ return http::BadRequest("'" + stringify(v.get()) + "' < original level.\n");
++ }
++
++ Try<Duration> d = Duration::parse(duration.get());
++
++ if (d.isError()) {
++ return http::BadRequest(d.error() + ".\n");
++ }
++
++ // Set the logging level.
++ set(v.get());
++
++ // Start a revert timer (if necessary).
++ if (v.get() != original) {
++ timeout = d.get();
++ delay(timeout.remaining(), this, &This::revert);
++ }
++
++ return http::OK();
++ }
++
++ void set(int v)
++ {
++ if (FLAGS_v != v) {
++ VLOG(FLAGS_v) << "Setting verbose logging level to " << v;
++ FLAGS_v = v;
++ __sync_synchronize(); // Ensure 'FLAGS_v' visible in other threads.
++ }
++ }
++
++ void revert()
++ {
++ if (timeout.remaining() == Seconds(0)) {
++ set(original);
++ }
++ }
++
++ static const std::string TOGGLE_HELP;
++
++ Timeout timeout;
++
++ const int32_t original; // Original value of FLAGS_v.
++};
++
++} // namespace process {
++
++#endif // __PROCESS_LOGGING_HPP__
+diff --git a/include/mesos/process/message.hpp b/include/mesos/process/message.hpp
+new file mode 100644
+index 0000000..c67c5e1
+--- /dev/null
++++ b/include/mesos/process/message.hpp
+@@ -0,0 +1,20 @@
++#ifndef __PROCESS_MESSAGE_HPP__
++#define __PROCESS_MESSAGE_HPP__
++
++#include <string>
++
++#include <process/pid.hpp>
++
++namespace process {
++
++struct Message
++{
++ std::string name;
++ UPID from;
++ UPID to;
++ std::string body;
++};
++
++} // namespace process {
++
++#endif // __PROCESS_MESSAGE_HPP__
+diff --git a/include/mesos/process/mime.hpp b/include/mesos/process/mime.hpp
+new file mode 100644
+index 0000000..0abeac1
+--- /dev/null
++++ b/include/mesos/process/mime.hpp
+@@ -0,0 +1,145 @@
++#ifndef __PROCESS_MIME_HPP__
++#define __PROCESS_MIME_HPP__
++
++namespace process {
++namespace mime {
++
++extern std::map<std::string, std::string> types;
++
++inline void initialize()
++{
++ // These MIME types were collected via:
++ /*
++ python -c '
++ import mimetypes
++ for extension, type in mimetypes.types_map.iteritems():
++ print "types[\"%s\"] = \"%s\";" % (extension, type)
++ '
++ */
++
++ types[".obj"] = "application/octet-stream";
++ types[".ra"] = "audio/x-pn-realaudio";
++ types[".wsdl"] = "application/xml";
++ types[".dll"] = "application/octet-stream";
++ types[".ras"] = "image/x-cmu-raster";
++ types[".ram"] = "application/x-pn-realaudio";
++ types[".bcpio"] = "application/x-bcpio";
++ types[".sh"] = "application/x-sh";
++ types[".m1v"] = "video/mpeg";
++ types[".xwd"] = "image/x-xwindowdump";
++ types[".doc"] = "application/msword";
++ types[".bmp"] = "image/x-ms-bmp";
++ types[".shar"] = "application/x-shar";
++ types[".js"] = "application/x-javascript";
++ types[".src"] = "application/x-wais-source";
++ types[".dvi"] = "application/x-dvi";
++ types[".aif"] = "audio/x-aiff";
++ types[".ksh"] = "text/plain";
++ types[".dot"] = "application/msword";
++ types[".mht"] = "message/rfc822";
++ types[".p12"] = "application/x-pkcs12";
++ types[".css"] = "text/css";
++ types[".csh"] = "application/x-csh";
++ types[".pwz"] = "application/vnd.ms-powerpoint";
++ types[".pdf"] = "application/pdf";
++ types[".cdf"] = "application/x-netcdf";
++ types[".pl"] = "text/plain";
++ types[".ai"] = "application/postscript";
++ types[".jpe"] = "image/jpeg";
++ types[".jpg"] = "image/jpeg";
++ types[".py"] = "text/x-python";
++ types[".xml"] = "text/xml";
++ types[".jpeg"] = "image/jpeg";
++ types[".ps"] = "application/postscript";
++ types[".gtar"] = "application/x-gtar";
++ types[".xpm"] = "image/x-xpixmap";
++ types[".hdf"] = "application/x-hdf";
++ types[".nws"] = "message/rfc822";
++ types[".tsv"] = "text/tab-separated-values";
++ types[".xpdl"] = "application/xml";
++ types[".p7c"] = "application/pkcs7-mime";
++ types[".eps"] = "application/postscript";
++ types[".ief"] = "image/ief";
++ types[".so"] = "application/octet-stream";
++ types[".xlb"] = "application/vnd.ms-excel";
++ types[".pbm"] = "image/x-portable-bitmap";
++ types[".texinfo"] = "application/x-texinfo";
++ types[".xls"] = "application/vnd.ms-excel";
++ types[".tex"] = "application/x-tex";
++ types[".rtx"] = "text/richtext";
++ types[".html"] = "text/html";
++ types[".aiff"] = "audio/x-aiff";
++ types[".aifc"] = "audio/x-aiff";
++ types[".exe"] = "application/octet-stream";
++ types[".sgm"] = "text/x-sgml";
++ types[".tif"] = "image/tiff";
++ types[".mpeg"] = "video/mpeg";
++ types[".ustar"] = "application/x-ustar";
++ types[".gif"] = "image/gif";
++ types[".ppt"] = "application/vnd.ms-powerpoint";
++ types[".pps"] = "application/vnd.ms-powerpoint";
++ types[".sgml"] = "text/x-sgml";
++ types[".ppm"] = "image/x-portable-pixmap";
++ types[".latex"] = "application/x-latex";
++ types[".bat"] = "text/plain";
++ types[".mov"] = "video/quicktime";
++ types[".ppa"] = "application/vnd.ms-powerpoint";
++ types[".tr"] = "application/x-troff";
++ types[".rdf"] = "application/xml";
++ types[".xsl"] = "application/xml";
++ types[".eml"] = "message/rfc822";
++ types[".nc"] = "application/x-netcdf";
++ types[".sv4cpio"] = "application/x-sv4cpio";
++ types[".bin"] = "application/octet-stream";
++ types[".h"] = "text/plain";
++ types[".tcl"] = "application/x-tcl";
++ types[".wiz"] = "application/msword";
++ types[".o"] = "application/octet-stream";
++ types[".a"] = "application/octet-stream";
++ types[".c"] = "text/plain";
++ types[".wav"] = "audio/x-wav";
++ types[".vcf"] = "text/x-vcard";
++ types[".xbm"] = "image/x-xbitmap";
++ types[".txt"] = "text/plain";
++ types[".au"] = "audio/basic";
++ types[".t"] = "application/x-troff";
++ types[".tiff"] = "image/tiff";
++ types[".texi"] = "application/x-texinfo";
++ types[".oda"] = "application/oda";
++ types[".ms"] = "application/x-troff-ms";
++ types[".rgb"] = "image/x-rgb";
++ types[".me"] = "application/x-troff-me";
++ types[".sv4crc"] = "application/x-sv4crc";
++ types[".qt"] = "video/quicktime";
++ types[".mpa"] = "video/mpeg";
++ types[".mpg"] = "video/mpeg";
++ types[".mpe"] = "video/mpeg";
++ types[".avi"] = "video/x-msvideo";
++ types[".pgm"] = "image/x-portable-graymap";
++ types[".pot"] = "application/vnd.ms-powerpoint";
++ types[".mif"] = "application/x-mif";
++ types[".roff"] = "application/x-troff";
++ types[".htm"] = "text/html";
++ types[".man"] = "application/x-troff-man";
++ types[".etx"] = "text/x-setext";
++ types[".zip"] = "application/zip";
++ types[".movie"] = "video/x-sgi-movie";
++ types[".pyc"] = "application/x-python-code";
++ types[".png"] = "image/png";
++ types[".pfx"] = "application/x-pkcs12";
++ types[".mhtml"] = "message/rfc822";
++ types[".tar"] = "application/x-tar";
++ types[".pnm"] = "image/x-portable-anymap";
++ types[".pyo"] = "application/x-python-code";
++ types[".snd"] = "audio/basic";
++ types[".cpio"] = "application/x-cpio";
++ types[".swf"] = "application/x-shockwave-flash";
++ types[".mp3"] = "audio/mpeg";
++ types[".mp2"] = "audio/mpeg";
++ types[".mp4"] = "video/mp4";
++}
++
++} // } namespace mime {
++} // } namespace process {
++
++#endif // __PROCESS_MIME_HPP__
+diff --git a/include/mesos/process/once.hpp b/include/mesos/process/once.hpp
+new file mode 100644
+index 0000000..e85b382
+--- /dev/null
++++ b/include/mesos/process/once.hpp
+@@ -0,0 +1,48 @@
++#ifndef __PROCESS_ONCE_HPP__
++#define __PROCESS_ONCE_HPP__
++
++#include <process/future.hpp>
++
++#include <stout/nothing.hpp>
++
++namespace process {
++
++// Provides a _blocking_ abstraction that's useful for performing a
++// task exactly once.
++class Once
++{
++public:
++ Once() {}
++
++ // Returns true if this Once instance has already transitioned to a
++ // 'done' state (i.e., the action you wanted to perform "once" has
++ // been completed). Note that this BLOCKS until Once::done has been
++ // called.
++ bool once()
++ {
++ if (!outer.set(&inner)) {
++ inner.future().await();
++ return true;
++ }
++
++ return false;
++ }
++
++ // Transitions this Once instance to a 'done' state.
++ void done()
++ {
++ inner.set(Nothing());
++ }
++
++private:
++ // Not copyable, not assignable.
++ Once(const Once& that);
++ Once& operator = (const Once& that);
++
++ Promise<Nothing> inner;
++ Promise<Promise<Nothing>*> outer;
++};
++
++} // namespace process {
++
++#endif // __PROCESS_ONCE_HPP__
+diff --git a/include/mesos/process/owned.hpp b/include/mesos/process/owned.hpp
+new file mode 100644
+index 0000000..4a03ea4
+--- /dev/null
++++ b/include/mesos/process/owned.hpp
+@@ -0,0 +1,166 @@
++#ifndef __PROCESS_OWNED_HPP__
++#define __PROCESS_OWNED_HPP__
++
++#include <glog/logging.h>
++
++#include <boost/shared_ptr.hpp>
++
++namespace process {
++
++// Forward declaration.
++template <typename T>
++class Shared;
++
++
++// Represents a uniquely owned pointer.
++//
++// TODO(bmahler): For now, Owned only provides shared_ptr semantics.
++// When we make the switch to C++11, we will change to provide
++// unique_ptr semantics. Consequently, each usage of Owned that
++// invoked a copy will have to be adjusted to use move semantics.
++template <typename T>
++class Owned
++{
++public:
++ Owned();
++ explicit Owned(T* t);
++
++ bool operator == (const Owned<T>& that) const;
++ bool operator < (const Owned<T>& that) const;
++
++ T& operator * () const;
++ T* operator -> () const;
++ T* get() const;
++
++ void reset();
++ void reset(T* t);
++ void swap(Owned<T>& that);
++
++ // Converts from an owned pointer to a shared pointer. This owned
++ // pointer will be reset after this function is invoked.
++ Shared<T> share();
++
++private:
++ struct Data
++ {
++ Data(T* t);
++ ~Data();
++
++ T* volatile t; // The pointer 't' is volatile.
++ };
++
++ boost::shared_ptr<Data> data;
++};
++
++
++template <typename T>
++Owned<T>::Owned() {}
++
++
++template <typename T>
++Owned<T>::Owned(T* t)
++{
++ if (t != NULL) {
++ data.reset(new Data(t));
++ }
++}
++
++
++template <typename T>
++bool Owned<T>::operator == (const Owned<T>& that) const
++{
++ return data == that.data;
++}
++
++
++template <typename T>
++bool Owned<T>::operator < (const Owned<T>& that) const
++{
++ return data < that.data;
++}
++
++
++template <typename T>
++T& Owned<T>::operator * () const
++{
++ return *CHECK_NOTNULL(get());
++}
++
++
++template <typename T>
++T* Owned<T>::operator -> () const
++{
++ return CHECK_NOTNULL(get());
++}
++
++
++template <typename T>
++T* Owned<T>::get() const
++{
++ if (data.get() == NULL) {
++ return NULL;
++ } else {
++ CHECK(data->t != NULL) << "This owned pointer has already been shared";
++
++ return data->t;
++ }
++}
++
++
++template <typename T>
++void Owned<T>::reset()
++{
++ data.reset();
++}
++
++
++template <typename T>
++void Owned<T>::reset(T* t)
++{
++ if (t == NULL) {
++ data.reset();
++ } else {
++ data.reset(new Data(t));
++ }
++}
++
++
++template <typename T>
++void Owned<T>::swap(Owned<T>& that)
++{
++ data.swap(that.data);
++}
++
++
++template <typename T>
++Shared<T> Owned<T>::share()
++{
++ if (data.get() == NULL) {
++ return Shared<T>(NULL);
++ }
++
++ // Atomically set the pointer 'data->t' to NULL.
++ T* t = __sync_fetch_and_and(&data->t, NULL);
++ CHECK(t != NULL) << "The ownership of this pointer has already been shared";
++
++ data.reset();
++ return Shared<T>(t);
++}
++
++
++template <typename T>
++Owned<T>::Data::Data(T* _t)
++ : t(CHECK_NOTNULL(_t)) {}
++
++
++template <typename T>
++Owned<T>::Data::~Data()
++{
++ if (t != NULL) {
++ delete t;
++ }
++}
++
++} // namespace process {
++
++#endif // __PROCESS_OWNED_HPP__
+diff --git a/include/mesos/process/pid.hpp b/include/mesos/process/pid.hpp
+new file mode 100644
+index 0000000..5a77dbc
+--- /dev/null
++++ b/include/mesos/process/pid.hpp
+@@ -0,0 +1,121 @@
++#ifndef __PROCESS_PID_HPP__
++#define __PROCESS_PID_HPP__
++
++#include <stdint.h>
++
++#include <iostream>
++#include <sstream>
++#include <string>
++
++
++namespace process {
++
++// Forward declaration to break cyclic dependencies.
++class ProcessBase;
++
++
++struct UPID
++{
++ UPID()
++ : ip(0), port(0) {}
++
++ UPID(const UPID& that)
++ : id(that.id), ip(that.ip), port(that.port) {}
++
++ UPID(const char* id_, uint32_t ip_, uint16_t port_)
++ : id(id_), ip(ip_), port(port_) {}
++
++ UPID(const std::string& id_, uint32_t ip_, uint16_t port_)
++ : id(id_), ip(ip_), port(port_) {}
++
++ UPID(const char* s);
++
++ UPID(const std::string& s);
++
++ UPID(const ProcessBase& process);
++
++ operator std::string () const;
++
++ operator bool () const
++ {
++ return id != "" && ip != 0 && port != 0;
++ }
++
++ bool operator ! () const
++ {
++ return id == "" && ip == 0 && port == 0;
++ }
++
++ bool operator < (const UPID& that) const
++ {
++ if (this != &that) {
++ if (ip == that.ip && port == that.port)
++ return id < that.id;
++ else if (ip == that.ip && port != that.port)
++ return port < that.port;
++ else
++ return ip < that.ip;
++ }
++
++ return false;
++ }
++
++ bool operator == (const UPID& that) const
++ {
++ if (this != &that) {
++ return (id == that.id &&
++ ip == that.ip &&
++ port == that.port);
++ }
++
++ return true;
++ }
++
++ bool operator != (const UPID& that) const
++ {
++ return !(this->operator == (that));
++ }
++
++ std::string id;
++ uint32_t ip;
++ uint16_t port;
++};
++
++
++template <typename T = ProcessBase>
++struct PID : UPID
++{
++ PID() : UPID() {}
++
++ PID(const T* t) : UPID(static_cast<const ProcessBase&>(*t)) {}
++ PID(const T& t) : UPID(static_cast<const ProcessBase&>(t)) {}
++
++ template <typename Base>
++ operator PID<Base> () const
++ {
++ // Only allow upcasts!
++ T* t = NULL;
++ Base* base = t;
++ (void)base; // Eliminate unused base warning.
++ PID<Base> pid;
++ pid.id = id;
++ pid.ip = ip;
++ pid.port = port;
++ return pid;
++ }
++};
++
++
++// Outputing UPIDs and generating UPIDs using streams.
++std::ostream& operator << (std::ostream&, const UPID&);
++std::istream& operator >> (std::istream&, UPID&);
++
++
++// UPID hash value (for example, to use in Boost's unordered maps).
++std::size_t hash_value(const UPID&);
++
++} // namespace process {
++
++
++
++#endif // __PROCESS_PID_HPP__
+diff --git a/include/mesos/process/process.hpp b/include/mesos/process/process.hpp
+new file mode 100644
+index 0000000..d9dc571
+--- /dev/null
++++ b/include/mesos/process/process.hpp
+@@ -0,0 +1,375 @@
++#ifndef __PROCESS_PROCESS_HPP__
++#define __PROCESS_PROCESS_HPP__
++
++#include <stdint.h>
++#include <pthread.h>
++
++#include <map>
++#include <queue>
++
++#include <tr1/functional>
++
++#include <process/clock.hpp>
++#include <process/event.hpp>
++#include <process/filter.hpp>
++#include <process/http.hpp>
++#include <process/message.hpp>
++#include <process/mime.hpp>
++#include <process/pid.hpp>
++
++#include <stout/duration.hpp>
++#include <stout/option.hpp>
++#include <stout/thread.hpp>
++
++namespace process {
++
++class ProcessBase : public EventVisitor
++{
++public:
++ ProcessBase(const std::string& id = "");
++
++ virtual ~ProcessBase();
++
++ UPID self() const { return pid; }
++
++protected:
++ // Invoked when an event is serviced.
++ virtual void serve(const Event& event)
++ {
++ event.visit(this);
++ }
++
++ // Callbacks used to visit (i.e., handle) a specific event.
++ virtual void visit(const MessageEvent& event);
++ virtual void visit(const DispatchEvent& event);
++ virtual void visit(const HttpEvent& event);
++ virtual void visit(const ExitedEvent& event);
++ virtual void visit(const TerminateEvent& event);
++
++ // Invoked when a process gets spawned.
++ virtual void initialize() {}
++
++ // Invoked when a process is terminated (unless visit is overriden).
++ virtual void finalize() {}
++
++ // Invoked when a linked process has exited (see link).
++ virtual void exited(const UPID& pid) {}
++
++ // Invoked when a linked process can no longer be monitored (see link).
++ virtual void lost(const UPID& pid) {}
++
++ // Puts a message at front of queue.
++ void inject(
++ const UPID& from,
++ const std::string& name,
++ const char* data = NULL,
++ size_t length = 0);
++
++ // Sends a message with data to PID.
++ void send(
++ const UPID& to,
++ const std::string& name,
++ const char* data = NULL,
++ size_t length = 0);
++
++ // Links with the specified PID. Linking with a process from within
++ // the same "operating system process" is gauranteed to give you
++ // perfect monitoring of that process. However, linking with a
++ // process on another machine might result in receiving lost
++ // callbacks due to the nature of a distributed environment.
++ UPID link(const UPID& pid);
++
++ // The default visit implementation for message events invokes
++ // installed message handlers, or delegates the message to another
++ // process (a delegate can be installed below but a message handler
++ // always takes precedence over delegating). A message handler is
++ // any function which takes two arguments, the "from" pid and the
++ // message body.
++ typedef std::tr1::function<void(const UPID&, const std::string&)>
++ MessageHandler;
++
++ // Setup a handler for a message.
++ void install(
++ const std::string& name,
++ const MessageHandler& handler)
++ {
++ handlers.message[name] = handler;
++ }
++
++ template <typename T>
++ void install(
++ const std::string& name,
++ void (T::*method)(const UPID&, const std::string&))
++ {
++ // Note that we use dynamic_cast here so a process can use
++ // multiple inheritance if it sees so fit (e.g., to implement
++ // multiple callback interfaces).
++ MessageHandler handler =
++ std::tr1::bind(method,
++ dynamic_cast<T*>(this),
++ std::tr1::placeholders::_1,
++ std::tr1::placeholders::_2);
++ install(name, handler);
++ }
++
++ // Delegate incoming message's with the specified name to pid.
++ void delegate(const std::string& name, const UPID& pid)
++ {
++ delegates[name] = pid;
++ }
++
++ // The default visit implementation for HTTP events invokes
++ // installed HTTP handlers. A HTTP handler is any function which
++ // takes an http::Request object and returns an http::Response.
++ typedef std::tr1::function<Future<http::Response>(const http::Request&)>
++ HttpRequestHandler;
++
++ // Setup a handler for an HTTP request.
++ bool route(
++ const std::string& name,
++ const Option<std::string>& help,
++ const HttpRequestHandler& handler);
++
++ template <typename T>
++ bool route(
++ const std::string& name,
++ const Option<std::string>& help,
++ Future<http::Response> (T::*method)(const http::Request&))
++ {
++ // Note that we use dynamic_cast here so a process can use
++ // multiple inheritance if it sees so fit (e.g., to implement
++ // multiple callback interfaces).
++ HttpRequestHandler handler =
++ std::tr1::bind(method, dynamic_cast<T*>(this),
++ std::tr1::placeholders::_1);
++ return route(name, help, handler);
++ }
++
++ // Provide the static asset(s) at the specified _absolute_ path for
++ // the specified name. For example, assuming the process named
++ // "server" invoked 'provide("name", "path")' then an HTTP request
++ // for '/server/name' would return the asset found at 'path'. If the
++ // specified path is a directory then an HTTP request for
++ // '/server/name/file' would return the asset found at
++ // '/path/file'. The 'Content-Type' header of the HTTP response will
++ // be set to the specified type given the file extension (you can
++ // manipulate this via the optional 'types' parameter).
++ void provide(
++ const std::string& name,
++ const std::string& path,
++ const std::map<std::string, std::string>& types = mime::types)
++ {
++ // TODO(benh): Check that name is only alphanumeric (i.e., has no
++ // '/') and that path is absolute.
++ Asset asset;
++ asset.path = path;
++ asset.types = types;
++ assets[name] = asset;
++ }
++
++private:
++ friend class SocketManager;
++ friend class ProcessManager;
++ friend class ProcessReference;
++ friend void* schedule(void*);
++
++ // Process states.
++ enum {
++ BOTTOM,
++ READY,
++ RUNNING,
++ BLOCKED,
++ TERMINATING,
++ TERMINATED
++ } state;
++
++ // Mutex protecting internals.
++ // TODO(benh): Consider replacing with a spinlock, on multi-core systems.
++ pthread_mutex_t m;
++ void lock() { pthread_mutex_lock(&m); }
++ void unlock() { pthread_mutex_unlock(&m); }
++
++ // Enqueue the specified message, request, or function call.
++ void enqueue(Event* event, bool inject = false);
++
++ // Queue of received events.
++ std::deque<Event*> events;
++
++ // Delegates for messages.
++ std::map<std::string, UPID> delegates;
++
++ // Handlers for messages and HTTP requests.
++ struct {
++ std::map<std::string, MessageHandler> message;
++ std::map<std::string, HttpRequestHandler> http;
++ } handlers;
++
++ // Definition of a static asset.
++ struct Asset
++ {
++ std::string path;
++ std::map<std::string, std::string> types;
++ };
++
++ // Static assets(s) to provide.
++ std::map<std::string, Asset> assets;
++
++ // Active references.
++ int refs;
++
++ // Process PID.
++ UPID pid;
++};
++
++
++template <typename T>
++class Process : public virtual ProcessBase {
++public:
++ virtual ~Process() {}
++
++ // Returns pid of process; valid even before calling spawn.
++ PID<T> self() const { return PID<T>(dynamic_cast<const T*>(this)); }
++
++protected:
++ // Useful typedefs for dispatch/delay/defer to self()/this.
++ typedef T Self;
++ typedef T This;
++};
++
++
++/**
++ * Initialize the library. Note that libprocess uses Google's glog and
++ * you can specify options for it (e.g., a logging directory) via
++ * environment variables (see the glog documentation for more
++ * information).
++ *
++ * @param delegate process to receive root HTTP requests
++ */
++void initialize(const std::string& delegate = "");
++
++
++/**
++ * Returns the IP address associated with this instance of the
++ * library.
++ */
++uint32_t ip();
++
++
++/**
++ * Returns the port associated with this instance of the library.
++ */
++uint16_t port();
++
++
++/**
++ * Spawn a new process.
++ *
++ * @param process process to be spawned
++ * @param manage boolean whether process should get garbage collected
++ */
++UPID spawn(ProcessBase* process, bool manage = false);
++
++template <typename T>
++PID<T> spawn(T* t, bool manage = false)
++{
++ // We save the pid before spawn is called because it's possible that
++ // the process has already been deleted after spawn returns (e.g.,
++ // if 'manage' is true).
++ PID<T> pid(t);
++
++ if (!spawn(static_cast<ProcessBase*>(t), manage)) {
++ return PID<T>();
++ }
++
++ return pid;
++}
++
++template <typename T>
++PID<T> spawn(T& t, bool manage = false)
++{
++ return spawn(&t, manage);
++}
++
++
++/**
++ * Send a TERMINATE message to a process, injecting the message ahead
++ * of all other messages queued up for that process if requested. Note
++ * that currently terminate only works for local processes (in the
++ * future we plan to make this more explicit via the use of a PID
++ * instead of a UPID).
++ *
++ * @param inject if true message will be put on front of message queue
++ */
++void terminate(const UPID& pid, bool inject = true);
++void terminate(const ProcessBase& process, bool inject = true);
++void terminate(const ProcessBase* process, bool inject = true);
++
++
++/**
++ * Wait for process to exit no more than specified seconds (returns
++ * true if actually waited on a process).
++ *
++ * @param PID id of the process
++ * @param secs max time to wait, 0 implies wait for ever
++ */
++bool wait(const UPID& pid, const Duration& duration = Seconds(-1));
++bool wait(const ProcessBase& process, const Duration& duration = Seconds(-1));
++bool wait(const ProcessBase* process, const Duration& duration = Seconds(-1));
++
++
++/**
++ * Sends a message with data without a return address.
++ *
++ * @param to receiver
++ * @param name message name
++ * @param data data to send (gets copied)
++ * @param length length of data
++ */
++void post(const UPID& to,
++ const std::string& name,
++ const char* data = NULL,
++ size_t length = 0);
++
++
++void post(const UPID& from,
++ const UPID& to,
++ const std::string& name,
++ const char* data = NULL,
++ size_t length = 0);
++
++
++// Inline implementations of above.
++inline void terminate(const ProcessBase& process, bool inject)
++{
++ terminate(process.self(), inject);
++}
++
++
++inline void terminate(const ProcessBase* process, bool inject)
++{
++ terminate(process->self(), inject);
++}
++
++
++inline bool wait(const ProcessBase& process, const Duration& duration)
++{
++ return process::wait(process.self(), duration); // Explicit to disambiguate.
++}
++
++
++inline bool wait(const ProcessBase* process, const Duration& duration)
++{
++ return process::wait(process->self(), duration); // Explicit to disambiguate.
++}
++
++
++// Per thread process pointer. The extra level of indirection from
++// _process_ to __process__ is used in order to take advantage of the
++// ThreadLocal operators without needing the extra dereference.
++extern ThreadLocal<ProcessBase>* _process_;
++
++#define __process__ (*_process_)
++
++} // namespace process {
++
++#endif // __PROCESS_PROCESS_HPP__
+diff --git a/include/mesos/process/profiler.hpp b/include/mesos/process/profiler.hpp
+new file mode 100644
+index 0000000..c886d7e
+--- /dev/null
++++ b/include/mesos/process/profiler.hpp
+@@ -0,0 +1,119 @@
++#ifndef __PROCESS_PROFILER_HPP__
++#define __PROCESS_PROFILER_HPP__
++
++#include <glog/logging.h>
++
++#ifdef HAS_GPERFTOOLS
++#include <gperftools/profiler.h>
++#endif
++
++#include <string>
++
++#include <process/future.hpp>
++#include <process/http.hpp>
++#include <process/process.hpp>
++
++#include <stout/format.hpp>
++#include <stout/os.hpp>
++
++namespace process {
++
++const std::string PROFILE_FILE = "perftools.out";
++
++class Profiler : public Process<Profiler>
++{
++public:
++ Profiler() : ProcessBase("profiler"), started(false) {}
++
++ virtual ~Profiler() {}
++
++protected:
++ virtual void initialize()
++ {
++ route("/start", START_HELP, &Profiler::start);
++ route("/stop", STOP_HELP, &Profiler::stop);
++ }
++
++private:
++ static const std::string START_HELP;
++ static const std::string STOP_HELP;
++
++ // HTTP endpoints.
++
++ // Starts the profiler. There are no request parameters.
++ Future<http::Response> start(const http::Request& request)
++ {
++#ifdef HAS_GPERFTOOLS
++ if (os::getenv("LIBPROCESS_ENABLE_PROFILER", false) != "1") {
++ return http::BadRequest(
++ "The profiler is not enabled. To enable the profiler, libprocess "
++ "must be started with LIBPROCESS_ENABLE_PROFILER=1 in the "
++ "environment.\n");
++ }
++
++ if (started) {
++ return http::BadRequest("Profiler already started.\n");
++ }
++
++ LOG(INFO) << "Starting Profiler";
++
++ // WARNING: If using libunwind < 1.0.1, profiling should not be used, as
++ // there are reports of crashes.
++ // WARNING: If using libunwind 1.0.1, profiling should not be turned on
++ // when it's possible for new threads to be created.
++ // This may cause a deadlock. The workaround used in libprocess is described
++ // here:
++ // https://groups.google.com/d/topic/google-perftools/Df10Uy4Djrg/discussion
++ // NOTE: We have not tested this with libunwind > 1.0.1.
++ if (!ProfilerStart(PROFILE_FILE.c_str())) {
++ std::string error =
++ strings::format("Failed to start profiler: %s", strerror(errno)).get();
++ LOG(ERROR) << error;
++ return http::InternalServerError(error);
++ }
++
++ started = true;
++ return http::OK("Profiler started.\n");
++#else
++ return http::BadRequest(
++ "Perftools is disabled. To enable perftools, "
++ "configure libprocess with --enable-perftools.\n");
++#endif
++ }
++
++ // Stops the profiler. There are no request parameters.
++ // This returns the profile output, it will also remain present
++ // in the working directory.
++ Future<http::Response> stop(const http::Request& request)
++ {
++#ifdef HAS_GPERFTOOLS
++ if (!started) {
++ return http::BadRequest("Profiler not running.\n");
++ }
++
++ LOG(INFO) << "Stopping Profiler";
++
++ ProfilerStop();
++
++ http::OK response;
++ response.type = response.PATH;
++ response.path = "perftools.out";
++ response.headers["Content-Type"] = "application/octet-stream";
++ response.headers["Content-Disposition"] =
++ strings::format("attachment; filename=%s", PROFILE_FILE).get();
++
++ started = false;
++ return response;
++#else
++ return http::BadRequest(
++ "Perftools is disabled. To enable perftools, "
++ "configure libprocess with --enable-perftools.\n");
++#endif
++ }
++
++ bool started;
++};
++
++} // namespace process {
++
++#endif // __PROCESS_PROCESS_HPP__
+diff --git a/include/mesos/process/protobuf.hpp b/include/mesos/process/protobuf.hpp
+new file mode 100644
+index 0000000..5a6fc83
+--- /dev/null
++++ b/include/mesos/process/protobuf.hpp
+@@ -0,0 +1,737 @@
++#ifndef __PROCESS_PROTOBUF_HPP__
++#define __PROCESS_PROTOBUF_HPP__
++
++#include <glog/logging.h>
++
++#include <google/protobuf/message.h>
++#include <google/protobuf/repeated_field.h>
++
++#include <set>
++#include <vector>
++
++#include <tr1/functional>
++#include <tr1/unordered_map>
++
++#include <process/dispatch.hpp>
++#include <process/process.hpp>
++
++#include <stout/lambda.hpp>
++
++
++// Provides an implementation of process::post that for a protobuf.
++namespace process {
++
++inline void post(const process::UPID& to,
++ const google::protobuf::Message& message)
++{
++ std::string data;
++ message.SerializeToString(&data);
++ post(to, message.GetTypeName(), data.data(), data.size());
++}
++
++
++inline void post(const process::UPID& from,
++ const process::UPID& to,
++ const google::protobuf::Message& message)
++{
++ std::string data;
++ message.SerializeToString(&data);
++ post(from, to, message.GetTypeName(), data.data(), data.size());
++}
++
++} // namespace process {
++
++
++// The rest of this file provides libprocess "support" for using
++// protocol buffers. In particular, this file defines a subclass of
++// Process (ProtobufProcess) that allows you to install protocol
++// buffer handlers in addition to normal message and HTTP
++// handlers. Install handlers can optionally take the sender's UPID
++// as their first argument.
++// Note that this header file assumes you will be linking
++// against BOTH libprotobuf and libglog.
++
++namespace google { namespace protobuf {
++
++// Type conversions helpful for changing between protocol buffer types
++// and standard C++ types (for parameters).
++template <typename T>
++const T& convert(const T& t)
++{
++ return t;
++}
++
++
++template <typename T>
++std::vector<T> convert(const google::protobuf::RepeatedPtrField<T>& items)
++{
++ std::vector<T> result;
++ for (int i = 0; i < items.size(); i++) {
++ result.push_back(items.Get(i));
++ }
++
++ return result;
++}
++
++}} // namespace google { namespace protobuf {
++
++
++template <typename T>
++class ProtobufProcess : public process::Process<T>
++{
++public:
++ virtual ~ProtobufProcess() {}
++
++protected:
++ virtual void visit(const process::MessageEvent& event)
++ {
++ if (protobufHandlers.count(event.message->name) > 0) {
++ from = event.message->from; // For 'reply'.
++ protobufHandlers[event.message->name](
++ event.message->from, event.message->body);
++ from = process::UPID();
++ } else {
++ process::Process<T>::visit(event);
++ }
++ }
++
++ void send(const process::UPID& to,
++ const google::protobuf::Message& message)
++ {
++ std::string data;
++ message.SerializeToString(&data);
++ process::Process<T>::send(to, message.GetTypeName(),
++ data.data(), data.size());
++ }
++
++ using process::Process<T>::send;
++
++ void reply(const google::protobuf::Message& message)
++ {
++ CHECK(from) << "Attempting to reply without a sender";
++ std::string data;
++ message.SerializeToString(&data);
++ send(from, message);
++ }
++
++ // Installs that take the sender as the first argument.
++ template <typename M>
++ void install(void (T::*method)(const process::UPID&, const M&))
++ {
++ google::protobuf::Message* m = new M();
++ T* t = static_cast<T*>(this);
++ protobufHandlers[m->GetTypeName()] =
++ lambda::bind(&handlerM<M>,
++ t, method,
++ lambda::_1, lambda::_2);
++ delete m;
++ }
++
++ template <typename M>
++ void install(void (T::*method)(const process::UPID&))
++ {
++ google::protobuf::Message* m = new M();
++ T* t = static_cast<T*>(this);
++ protobufHandlers[m->GetTypeName()] =
++ lambda::bind(&handler0,
++ t, method,
++ lambda::_1, lambda::_2);
++ delete m;
++ }
++
++ template <typename M,
++ typename P1, typename P1C>
++ void install(
++ void (T::*method)(const process::UPID&, P1C),
++ P1 (M::*param1)() const)
++ {
++ google::protobuf::Message* m = new M();
++ T* t = static_cast<T*>(this);
++ protobufHandlers[m->GetTypeName()] =
++ lambda::bind(&handler1<M, P1, P1C>,
++ t, method, param1,
++ lambda::_1, lambda::_2);
++ delete m;
++ }
++
++ template <typename M,
++ typename P1, typename P1C,
++ typename P2, typename P2C>
++ void install(
++ void (T::*method)(const process::UPID&, P1C, P2C),
++ P1 (M::*p1)() const,
++ P2 (M::*p2)() const)
++ {
++ google::protobuf::Message* m = new M();
++ T* t = static_cast<T*>(this);
++ protobufHandlers[m->GetTypeName()] =
++ lambda::bind(&handler2<M, P1, P1C, P2, P2C>,
++ t, method, p1, p2,
++ lambda::_1, lambda::_2);
++ delete m;
++ }
++
++ template <typename M,
++ typename P1, typename P1C,
++ typename P2, typename P2C,
++ typename P3, typename P3C>
++ void install(
++ void (T::*method)(const process::UPID&, P1C, P2C, P3C),
++ P1 (M::*p1)() const,
++ P2 (M::*p2)() const,
++ P3 (M::*p3)() const)
++ {
++ google::protobuf::Message* m = new M();
++ T* t = static_cast<T*>(this);
++ protobufHandlers[m->GetTypeName()] =
++ lambda::bind(&handler3<M, P1, P1C, P2, P2C, P3, P3C>,
++ t, method, p1, p2, p3,
++ lambda::_1, lambda::_2);
++ delete m;
++ }
++
++ template <typename M,
++ typename P1, typename P1C,
++ typename P2, typename P2C,
++ typename P3, typename P3C,
++ typename P4, typename P4C>
++ void install(
++ void (T::*method)(const process::UPID&, P1C, P2C, P3C, P4C),
++ P1 (M::*p1)() const,
++ P2 (M::*p2)() const,
++ P3 (M::*p3)() const,
++ P4 (M::*p4)() const)
++ {
++ google::protobuf::Message* m = new M();
++ T* t = static_cast<T*>(this);
++ protobufHandlers[m->GetTypeName()] =
++ lambda::bind(&handler4<M, P1, P1C, P2, P2C, P3, P3C, P4, P4C>,
++ t, method, p1, p2, p3, p4,
++ lambda::_1, lambda::_2);
++ delete m;
++ }
++
++ template <typename M,
++ typename P1, typename P1C,
++ typename P2, typename P2C,
++ typename P3, typename P3C,
++ typename P4, typename P4C,
++ typename P5, typename P5C>
++ void install(
++ void (T::*method)(const process::UPID&, P1C, P2C, P3C, P4C, P5C),
++ P1 (M::*p1)() const,
++ P2 (M::*p2)() const,
++ P3 (M::*p3)() const,
++ P4 (M::*p4)() const,
++ P5 (M::*p5)() const)
++ {
++ google::protobuf::Message* m = new M();
++ T* t = static_cast<T*>(this);
++ protobufHandlers[m->GetTypeName()] =
++ lambda::bind(&handler5<M, P1, P1C, P2, P2C, P3, P3C, P4, P4C, P5, P5C>,
++ t, method, p1, p2, p3, p4, p5,
++ lambda::_1, lambda::_2);
++ delete m;
++ }
++
++ // Installs that do not take the sender.
++ template <typename M>
++ void install(void (T::*method)(const M&))
++ {
++ google::protobuf::Message* m = new M();
++ T* t = static_cast<T*>(this);
++ protobufHandlers[m->GetTypeName()] =
++ lambda::bind(&_handlerM<M>,
++ t, method,
++ lambda::_1, lambda::_2);
++ delete m;
++ }
++
++ template <typename M>
++ void install(void (T::*method)())
++ {
++ google::protobuf::Message* m = new M();
++ T* t = static_cast<T*>(this);
++ protobufHandlers[m->GetTypeName()] =
++ lambda::bind(&_handler0,
++ t, method,
++ lambda::_1, lambda::_2);
++ delete m;
++ }
++
++ template <typename M,
++ typename P1, typename P1C>
++ void install(
++ void (T::*method)(P1C),
++ P1 (M::*param1)() const)
++ {
++ google::protobuf::Message* m = new M();
++ T* t = static_cast<T*>(this);
++ protobufHandlers[m->GetTypeName()] =
++ lambda::bind(&_handler1<M, P1, P1C>,
++ t, method, param1,
++ lambda::_1, lambda::_2);
++ delete m;
++ }
++
++ template <typename M,
++ typename P1, typename P1C,
++ typename P2, typename P2C>
++ void install(
++ void (T::*method)(P1C, P2C),
++ P1 (M::*p1)() const,
++ P2 (M::*p2)() const)
++ {
++ google::protobuf::Message* m = new M();
++ T* t = static_cast<T*>(this);
++ protobufHandlers[m->GetTypeName()] =
++ lambda::bind(&_handler2<M, P1, P1C, P2, P2C>,
++ t, method, p1, p2,
++ lambda::_1, lambda::_2);
++ delete m;
++ }
++
++ template <typename M,
++ typename P1, typename P1C,
++ typename P2, typename P2C,
++ typename P3, typename P3C>
++ void install(
++ void (T::*method)(P1C, P2C, P3C),
++ P1 (M::*p1)() const,
++ P2 (M::*p2)() const,
++ P3 (M::*p3)() const)
++ {
++ google::protobuf::Message* m = new M();
++ T* t = static_cast<T*>(this);
++ protobufHandlers[m->GetTypeName()] =
++ lambda::bind(&_handler3<M, P1, P1C, P2, P2C, P3, P3C>,
++ t, method, p1, p2, p3,
++ lambda::_1, lambda::_2);
++ delete m;
++ }
++
++ template <typename M,
++ typename P1, typename P1C,
++ typename P2, typename P2C,
++ typename P3, typename P3C,
++ typename P4, typename P4C>
++ void install(
++ void (T::*method)(P1C, P2C, P3C, P4C),
++ P1 (M::*p1)() const,
++ P2 (M::*p2)() const,
++ P3 (M::*p3)() const,
++ P4 (M::*p4)() const)
++ {
++ google::protobuf::Message* m = new M();
++ T* t = static_cast<T*>(this);
++ protobufHandlers[m->GetTypeName()] =
++ lambda::bind(&_handler4<M, P1, P1C, P2, P2C, P3, P3C, P4, P4C>,
++ t, method, p1, p2, p3, p4,
++ lambda::_1, lambda::_2);
++ delete m;
++ }
++
++ template <typename M,
++ typename P1, typename P1C,
++ typename P2, typename P2C,
++ typename P3, typename P3C,
++ typename P4, typename P4C,
++ typename P5, typename P5C>
++ void install(
++ void (T::*method)(P1C, P2C, P3C, P4C, P5C),
++ P1 (M::*p1)() const,
++ P2 (M::*p2)() const,
++ P3 (M::*p3)() const,
++ P4 (M::*p4)() const,
++ P5 (M::*p5)() const)
++ {
++ google::protobuf::Message* m = new M();
++ T* t = static_cast<T*>(this);
++ protobufHandlers[m->GetTypeName()] =
++ lambda::bind(&_handler5<M, P1, P1C, P2, P2C, P3, P3C, P4, P4C, P5, P5C>,
++ t, method, p1, p2, p3, p4, p5,
++ lambda::_1, lambda::_2);
++ delete m;
++ }
++
++ using process::Process<T>::install;
++
++private:
++ // Handlers that take the sender as the first argument.
++ template <typename M>
++ static void handlerM(
++ T* t,
++ void (T::*method)(const process::UPID&, const M&),
++ const process::UPID& sender,
++ const std::string& data)
++ {
++ M m;
++ m.ParseFromString(data);
++ if (m.IsInitialized()) {
++ (t->*method)(sender, m);
++ } else {
++ LOG(WARNING) << "Initialization errors: "
++ << m.InitializationErrorString();
++ }
++ }
++
++ static void handler0(
++ T* t,
++ void (T::*method)(const process::UPID&),
++ const process::UPID& sender,
++ const std::string& data)
++ {
++ (t->*method)(sender);
++ }
++
++ template <typename M,
++ typename P1, typename P1C>
++ static void handler1(
++ T* t,
++ void (T::*method)(const process::UPID&, P1C),
++ P1 (M::*p1)() const,
++ const process::UPID& sender,
++ const std::string& data)
++ {
++ M m;
++ m.ParseFromString(data);
++ if (m.IsInitialized()) {
++ (t->*method)(sender, google::protobuf::convert((&m->*p1)()));
++ } else {
++ LOG(WARNING) << "Initialization errors: "
++ << m.InitializationErrorString();
++ }
++ }
++
++ template <typename M,
++ typename P1, typename P1C,
++ typename P2, typename P2C>
++ static void handler2(
++ T* t,
++ void (T::*method)(const process::UPID&, P1C, P2C),
++ P1 (M::*p1)() const,
++ P2 (M::*p2)() const,
++ const process::UPID& sender,
++ const std::string& data)
++ {
++ M m;
++ m.ParseFromString(data);
++ if (m.IsInitialized()) {
++ (t->*method)(sender,
++ google::protobuf::convert((&m->*p1)()),
++ google::protobuf::convert((&m->*p2)()));
++ } else {
++ LOG(WARNING) << "Initialization errors: "
++ << m.InitializationErrorString();
++ }
++ }
++
++ template <typename M,
++ typename P1, typename P1C,
++ typename P2, typename P2C,
++ typename P3, typename P3C>
++ static void handler3(
++ T* t,
++ void (T::*method)(const process::UPID&, P1C, P2C, P3C),
++ P1 (M::*p1)() const,
++ P2 (M::*p2)() const,
++ P3 (M::*p3)() const,
++ const process::UPID& sender,
++ const std::string& data)
++ {
++ M m;
++ m.ParseFromString(data);
++ if (m.IsInitialized()) {
++ (t->*method)(sender,
++ google::protobuf::convert((&m->*p1)()),
++ google::protobuf::convert((&m->*p2)()),
++ google::protobuf::convert((&m->*p3)()));
++ } else {
++ LOG(WARNING) << "Initialization errors: "
++ << m.InitializationErrorString();
++ }
++ }
++
++ template <typename M,
++ typename P1, typename P1C,
++ typename P2, typename P2C,
++ typename P3, typename P3C,
++ typename P4, typename P4C>
++ static void handler4(
++ T* t,
++ void (T::*method)(const process::UPID&, P1C, P2C, P3C, P4C),
++ P1 (M::*p1)() const,
++ P2 (M::*p2)() const,
++ P3 (M::*p3)() const,
++ P4 (M::*p4)() const,
++ const process::UPID& sender,
++ const std::string& data)
++ {
++ M m;
++ m.ParseFromString(data);
++ if (m.IsInitialized()) {
++ (t->*method)(sender,
++ google::protobuf::convert((&m->*p1)()),
++ google::protobuf::convert((&m->*p2)()),
++ google::protobuf::convert((&m->*p3)()),
++ google::protobuf::convert((&m->*p4)()));
++ } else {
++ LOG(WARNING) << "Initialization errors: "
++ << m.InitializationErrorString();
++ }
++ }
++
++ template <typename M,
++ typename P1, typename P1C,
++ typename P2, typename P2C,
++ typename P3, typename P3C,
++ typename P4, typename P4C,
++ typename P5, typename P5C>
++ static void handler5(
++ T* t,
++ void (T::*method)(const process::UPID&, P1C, P2C, P3C, P4C, P5C),
++ P1 (M::*p1)() const,
++ P2 (M::*p2)() const,
++ P3 (M::*p3)() const,
++ P4 (M::*p4)() const,
++ P5 (M::*p5)() const,
++ const process::UPID& sender,
++ const std::string& data)
++ {
++ M m;
++ m.ParseFromString(data);
++ if (m.IsInitialized()) {
++ (t->*method)(sender,
++ google::protobuf::convert((&m->*p1)()),
++ google::protobuf::convert((&m->*p2)()),
++ google::protobuf::convert((&m->*p3)()),
++ google::protobuf::convert((&m->*p4)()),
++ google::protobuf::convert((&m->*p5)()));
++ } else {
++ LOG(WARNING) << "Initialization errors: "
++ << m.InitializationErrorString();
++ }
++ }
++
++ // Handlers that ignore the sender.
++ template <typename M>
++ static void _handlerM(
++ T* t,
++ void (T::*method)(const M&),
++ const process::UPID&,
++ const std::string& data)
++ {
++ M m;
++ m.ParseFromString(data);
++ if (m.IsInitialized()) {
++ (t->*method)(m);
++ } else {
++ LOG(WARNING) << "Initialization errors: "
++ << m.InitializationErrorString();
++ }
++ }
++
++ static void _handler0(
++ T* t,
++ void (T::*method)(),
++ const process::UPID&,
++ const std::string& data)
++ {
++ (t->*method)();
++ }
++
++ template <typename M,
++ typename P1, typename P1C>
++ static void _handler1(
++ T* t,
++ void (T::*method)(P1C),
++ P1 (M::*p1)() const,
++ const process::UPID&,
++ const std::string& data)
++ {
++ M m;
++ m.ParseFromString(data);
++ if (m.IsInitialized()) {
++ (t->*method)(google::protobuf::convert((&m->*p1)()));
++ } else {
++ LOG(WARNING) << "Initialization errors: "
++ << m.InitializationErrorString();
++ }
++ }
++
++ template <typename M,
++ typename P1, typename P1C,
++ typename P2, typename P2C>
++ static void _handler2(
++ T* t,
++ void (T::*method)(P1C, P2C),
++ P1 (M::*p1)() const,
++ P2 (M::*p2)() const,
++ const process::UPID&,
++ const std::string& data)
++ {
++ M m;
++ m.ParseFromString(data);
++ if (m.IsInitialized()) {
++ (t->*method)(google::protobuf::convert((&m->*p1)()),
++ google::protobuf::convert((&m->*p2)()));
++ } else {
++ LOG(WARNING) << "Initialization errors: "
++ << m.InitializationErrorString();
++ }
++ }
++
++ template <typename M,
++ typename P1, typename P1C,
++ typename P2, typename P2C,
++ typename P3, typename P3C>
++ static void _handler3(
++ T* t,
++ void (T::*method)(P1C, P2C, P3C),
++ P1 (M::*p1)() const,
++ P2 (M::*p2)() const,
++ P3 (M::*p3)() const,
++ const process::UPID&,
++ const std::string& data)
++ {
++ M m;
++ m.ParseFromString(data);
++ if (m.IsInitialized()) {
++ (t->*method)(google::protobuf::convert((&m->*p1)()),
++ google::protobuf::convert((&m->*p2)()),
++ google::protobuf::convert((&m->*p3)()));
++ } else {
++ LOG(WARNING) << "Initialization errors: "
++ << m.InitializationErrorString();
++ }
++ }
++
++ template <typename M,
++ typename P1, typename P1C,
++ typename P2, typename P2C,
++ typename P3, typename P3C,
++ typename P4, typename P4C>
++ static void _handler4(
++ T* t,
++ void (T::*method)(P1C, P2C, P3C, P4C),
++ P1 (M::*p1)() const,
++ P2 (M::*p2)() const,
++ P3 (M::*p3)() const,
++ P4 (M::*p4)() const,
++ const process::UPID&,
++ const std::string& data)
++ {
++ M m;
++ m.ParseFromString(data);
++ if (m.IsInitialized()) {
++ (t->*method)(google::protobuf::convert((&m->*p1)()),
++ google::protobuf::convert((&m->*p2)()),
++ google::protobuf::convert((&m->*p3)()),
++ google::protobuf::convert((&m->*p4)()));
++ } else {
++ LOG(WARNING) << "Initialization errors: "
++ << m.InitializationErrorString();
++ }
++ }
++
++ template <typename M,
++ typename P1, typename P1C,
++ typename P2, typename P2C,
++ typename P3, typename P3C,
++ typename P4, typename P4C,
++ typename P5, typename P5C>
++ static void _handler5(
++ T* t,
++ void (T::*method)(P1C, P2C, P3C, P4C, P5C),
++ P1 (M::*p1)() const,
++ P2 (M::*p2)() const,
++ P3 (M::*p3)() const,
++ P4 (M::*p4)() const,
++ P5 (M::*p5)() const,
++ const process::UPID&,
++ const std::string& data)
++ {
++ M m;
++ m.ParseFromString(data);
++ if (m.IsInitialized()) {
++ (t->*method)(google::protobuf::convert((&m->*p1)()),
++ google::protobuf::convert((&m->*p2)()),
++ google::protobuf::convert((&m->*p3)()),
++ google::protobuf::convert((&m->*p4)()),
++ google::protobuf::convert((&m->*p5)()));
++ } else {
++ LOG(WARNING) << "Initialization errors: "
++ << m.InitializationErrorString();
++ }
++ }
++
++ typedef lambda::function<
++ void(const process::UPID&, const std::string&)> handler;
++ std::tr1::unordered_map<std::string, handler> protobufHandlers;
++
++ // Sender of "current" message, inaccessible by subclasses.
++ // This is only used for reply().
++ process::UPID from;
++};
++
++
++// Implements a process for sending protobuf "requests" to a process
++// and waiting for a protobuf "response", but uses futures so that
++// this can be done without needing to implement a process.
++template <typename Req, typename Res>
++class ReqResProcess : public ProtobufProcess<ReqResProcess<Req, Res> >
++{
++public:
++ ReqResProcess(const process::UPID& _pid, const Req& _req)
++ : pid(_pid), req(_req)
++ {
++ ProtobufProcess<ReqResProcess<Req, Res> >::template
++ install<Res>(&ReqResProcess<Req, Res>::response);
++ }
++
++ process::Future<Res> run()
++ {
++ // Terminate this process if no one cares about the response
++ // (note, we need to disambiguate the process::terminate).
++ void (*terminate)(const process::UPID&, bool) = &process::terminate;
++ promise.future().onDiscarded(
++ lambda::bind(terminate, process::ProcessBase::self(), true));
++
++ ProtobufProcess<ReqResProcess<Req, Res> >::send(pid, req);
++
++ return promise.future();
++ }
++
++private:
++ void response(const Res& res)
++ {
++ promise.set(res);
++ process::terminate(process::ProcessBase::self());
++ }
++
++ const process::UPID pid;
++ const Req req;
++ process::Promise<Res> promise;
++};
++
++
++// Allows you to describe request/response protocols and then use
++// those for sending requests and getting back responses.
++template <typename Req, typename Res>
++struct Protocol
++{
++ process::Future<Res> operator () (
++ const process::UPID& pid,
++ const Req& req) const
++ {
++ // Help debugging by adding some "type constraints".
++ { Req* req = NULL; google::protobuf::Message* m = req; (void)m; }
++ { Res* res = NULL; google::protobuf::Message* m = res; (void)m; }
++
++ ReqResProcess<Req, Res>* process = new ReqResProcess<Req, Res>(pid, req);
++ process::spawn(process, true);
++ return process::dispatch(process, &ReqResProcess<Req, Res>::run);
++ }
++};
++
++#endif // __PROCESS_PROTOBUF_HPP__
+diff --git a/include/mesos/process/run.hpp b/include/mesos/process/run.hpp
+new file mode 100644
+index 0000000..a245b70
+--- /dev/null
++++ b/include/mesos/process/run.hpp
+@@ -0,0 +1,80 @@
++#ifndef __PROCESS_RUN_HPP__
++#define __PROCESS_RUN_HPP__
++
++#include <tr1/memory> // TODO(benh): Replace shared_ptr with unique_ptr.
++
++#include <process/process.hpp>
++
++#include <stout/preprocessor.hpp>
++
++namespace process {
++
++namespace internal {
++
++template <typename R>
++class ThunkProcess : public Process<ThunkProcess<R> >
++{
++public:
++ ThunkProcess(std::tr1::shared_ptr<std::tr1::function<R(void)> > _thunk,
++ std::tr1::shared_ptr<Promise<R> > _promise)
++ : thunk(_thunk),
++ promise(_promise) {}
++
++ virtual ~ThunkProcess() {}
++
++protected:
++ virtual void serve(const Event& event)
++ {
++ promise->set((*thunk)());
++ }
++
++private:
++ std::tr1::shared_ptr<std::tr1::function<R(void)> > thunk;
++ std::tr1::shared_ptr<Promise<R> > promise;
++};
++
++} // namespace internal {
++
++
++template <typename R>
++Future<R> run(R (*method)(void))
++{
++ std::tr1::shared_ptr<std::tr1::function<R(void)> > thunk(
++ new std::tr1::function<R(void)>(
++ std::tr1::bind(method)));
++
++ std::tr1::shared_ptr<Promise<R> > promise(new Promise<R>());
++ Future<R> future = promise->future();
++
++ terminate(spawn(new internal::ThunkProcess<R>(thunk, promise), true));
++
++ return future;
++}
++
++
++#define TEMPLATE(Z, N, DATA) \
++ template <typename R, \
++ ENUM_PARAMS(N, typename P), \
++ ENUM_PARAMS(N, typename A)> \
++ Future<R> run( \
++ R (*method)(ENUM_PARAMS(N, P)), \
++ ENUM_BINARY_PARAMS(N, A, a)) \
++ { \
++ std::tr1::shared_ptr<std::tr1::function<R(void)> > thunk( \
++ new std::tr1::function<R(void)>( \
++ std::tr1::bind(method, ENUM_PARAMS(N, a)))); \
++ \
++ std::tr1::shared_ptr<Promise<R> > promise(new Promise<R>()); \
++ Future<R> future = promise->future(); \
++ \
++ terminate(spawn(new internal::ThunkProcess<R>(thunk, promise), true)); \
++ \
++ return future; \
++ }
++
++ REPEAT_FROM_TO(1, 11, TEMPLATE, _) // Args A0 -> A9.
++#undef TEMPLATE
++
++} // namespace process {
++
++#endif // __PROCESS_RUN_HPP__
+diff --git a/include/mesos/process/shared.hpp b/include/mesos/process/shared.hpp
+new file mode 100644
+index 0000000..8f5b59b
+--- /dev/null
++++ b/include/mesos/process/shared.hpp
+@@ -0,0 +1,183 @@
++#ifndef __PROCESS_SHARED_HPP__
++#define __PROCESS_SHARED_HPP__
++
++#include <glog/logging.h>
++
++#include <boost/shared_ptr.hpp>
++
++#include <process/future.hpp>
++
++namespace process {
++
++// Forward declaration.
++template <typename T>
++class Owned;
++
++
++// Represents a shared pointer and therefore enforces 'const' access.
++template <typename T>
++class Shared
++{
++public:
++ Shared();
++ explicit Shared(T* t);
++
++ bool operator == (const Shared<T>& that) const;
++ bool operator < (const Shared<T>& that) const;
++
++ // Enforces const access semantics.
++ const T& operator * () const;
++ const T* operator -> () const;
++ const T* get() const;
++
++ bool unique() const;
++
++ void reset();
++ void reset(T* t);
++ void swap(Shared<T>& that);
++
++ // Transfers ownership of the pointer by waiting for exclusive
++ // access (i.e., no other Shared instances). This shared pointer
++ // will be reset after this function is invoked. If multiple shared
++ // pointers pointing to the same object all want to be upgraded,
++ // only one of them may succeed and the rest will get failures.
++ Future<Owned<T> > own();
++
++private:
++ struct Data
++ {
++ Data(T* _t);
++ ~Data();
++
++ T* t;
++ volatile bool owned;
++ Promise<Owned<T> > promise;
++ };
++
++ boost::shared_ptr<Data> data;
++};
++
++
++template <typename T>
++Shared<T>::Shared() {}
++
++
++template <typename T>
++Shared<T>::Shared(T* t)
++{
++ if (t != NULL) {
++ data.reset(new Data(t));
++ }
++}
++
++
++template <typename T>
++bool Shared<T>::operator == (const Shared<T>& that) const
++{
++ return data == that.data;
++}
++
++
++template <typename T>
++bool Shared<T>::operator < (const Shared<T>& that) const
++{
++ return data < that.data;
++}
++
++
++template <typename T>
++const T& Shared<T>::operator * () const
++{
++ return *CHECK_NOTNULL(get());
++}
++
++
++template <typename T>
++const T* Shared<T>::operator -> () const
++{
++ return CHECK_NOTNULL(get());
++}
++
++
++template <typename T>
++const T* Shared<T>::get() const
++{
++ if (data.get() == NULL) {
++ return NULL;
++ } else {
++ return data->t;
++ }
++}
++
++
++template <typename T>
++bool Shared<T>::unique() const
++{
++ return data.unique();
++}
++
++
++template <typename T>
++void Shared<T>::reset()
++{
++ data.reset();
++}
++
++
++template <typename T>
++void Shared<T>::reset(T* t)
++{
++ if (t == NULL) {
++ data.reset();
++ } else {
++ data.reset(new Data(t));
++ }
++}
++
++
++template <typename T>
++void Shared<T>::swap(Shared<T>& that)
++{
++ data.swap(that.data);
++}
++
++
++template <typename T>
++Future<Owned<T> > Shared<T>::own()
++{
++ // If two threads simultaneously access this object and at least one
++ // of them is a write, the behavior is undefined. This is similar to
++ // boost::shared_ptr. For more details, please refer to the boost
++ // shared_ptr document (section "Thread Safety").
++ if (data.get() == NULL) {
++ return Owned<T>(NULL);
++ }
++
++ if (!__sync_bool_compare_and_swap(&data->owned, false, true)) {
++ return Failure("Ownership has already been transferred");
++ }
++
++ Future<Owned<T> > future = data->promise.future();
++ data.reset();
++ return future;
++}
++
++
++template <typename T>
++Shared<T>::Data::Data(T* _t)
++ : t(CHECK_NOTNULL(_t)), owned(false) {}
++
++
++template <typename T>
++Shared<T>::Data::~Data()
++{
++ if (owned) {
++ promise.set(Owned<T>(t));
++ } else {
++ delete t;
++ }
++}
++
++} // namespace process {
++
++#endif // __PROCESS_SHARED_HPP__
+diff --git a/include/mesos/process/socket.hpp b/include/mesos/process/socket.hpp
+new file mode 100644
+index 0000000..669a333
+--- /dev/null
++++ b/include/mesos/process/socket.hpp
+@@ -0,0 +1,84 @@
++#ifndef __PROCESS_SOCKET_HPP__
++#define __PROCESS_SOCKET_HPP__
++
++#include <assert.h>
++#include <unistd.h> // For close.
++
++#include <iostream>
++
++#include <stout/nothing.hpp>
++#include <stout/os.hpp>
++#include <stout/try.hpp>
++
++// An abstraction around a socket (file descriptor) that provides
++// reference counting such that the socket is only closed (and thus,
++// has the possiblity of being reused) after there are no more
++// references.
++
++class Socket
++{
++public:
++ Socket()
++ : refs(new int(1)), s(-1) {}
++
++ explicit Socket(int _s)
++ : refs(new int(1)), s(_s) {}
++
++ ~Socket()
++ {
++ cleanup();
++ }
++
++ Socket(const Socket& that)
++ {
++ copy(that);
++ }
++
++ Socket& operator = (const Socket& that)
++ {
++ if (this != &that) {
++ cleanup();
++ copy(that);
++ }
++ return *this;
++ }
++
++ bool operator == (const Socket& that) const
++ {
++ return s == that.s && refs == that.refs;
++ }
++
++ operator int () const
++ {
++ return s;
++ }
++
++private:
++ void copy(const Socket& that)
++ {
++ assert(that.refs > 0);
++ __sync_fetch_and_add(that.refs, 1);
++ refs = that.refs;
++ s = that.s;
++ }
++
++ void cleanup()
++ {
++ assert(refs != NULL);
++ if (__sync_sub_and_fetch(refs, 1) == 0) {
++ delete refs;
++ if (s >= 0) {
++ Try<Nothing> close = os::close(s);
++ if (close.isError()) {
++ std::cerr << "Failed to close socket: " << close.error() << std::endl;
++ abort();
++ }
++ }
++ }
++ }
++
++ int* refs;
++ int s;
++};
++
++#endif // __PROCESS_SOCKET_HPP__
+diff --git a/include/mesos/process/statistics.hpp b/include/mesos/process/statistics.hpp
+new file mode 100644
+index 0000000..ce122a5
+--- /dev/null
++++ b/include/mesos/process/statistics.hpp
+@@ -0,0 +1,160 @@
++#ifndef __PROCESS_STATISTICS_HPP__
++#define __PROCESS_STATISTICS_HPP__
++
++#include <process/clock.hpp>
++#include <process/future.hpp>
++#include <process/owned.hpp>
++#include <process/time.hpp>
++
++#include <stout/duration.hpp>
++#include <stout/none.hpp>
++#include <stout/nothing.hpp>
++#include <stout/option.hpp>
++
++namespace process {
++
++// Forward declarations.
++class Statistics;
++class StatisticsProcess;
++
++namespace meters {
++ class Meter;
++ class TimeRate;
++}
++
++
++// Libprocess statistics handle.
++// To be used from anywhere to manage statistics.
++//
++// Ex: process::statistics->increment("http", "num_requests");
++// process::statistics->set("http", "response_size", response.size());
++//
++// Statistics are exposed via JSON for external visibility.
++extern Statistics* statistics;
++
++const Duration STATISTICS_TRUNCATION_INTERVAL = Minutes(5);
++
++// Provides an in-memory time series of statistics over some window
++// (values are truncated outside of the window, but no limit is
++// currently placed on the number of values within a window).
++//
++// TODO(bmahler): Time series granularity should be coarsened over
++// time. This means, for high-frequency statistics, we keep a lot of
++// recent data points (fine granularity), and keep fewer older data
++// points (coarse granularity). The tunable bit here could be the
++// total number of data points to keep around, which informs how
++// often to delete older data points, while still keeping a window
++// worth of data.
++class Statistics
++{
++public:
++ Statistics(const Duration& window);
++ ~Statistics();
++
++ // Returns the time series of a statistic.
++ process::Future<std::map<Time, double> > timeseries(
++ const std::string& context,
++ const std::string& name,
++ const Option<Time>& start = None(),
++ const Option<Time>& stop = None());
++
++ // Returns the latest value of a statistic.
++ process::Future<Option<double> > get(
++ const std::string& context,
++ const std::string& name);
++
++ // Returns the latest values of all statistics in the context.
++ process::Future<std::map<std::string, double> > get(
++ const std::string& context);
++
++ // Adds a meter for the statistic with the provided context and name.
++ // get(context, meter->name) will return the metered time series.
++ // Returns an error if:
++ // -meter->name == name, or
++ // -The meter already exists.
++ Future<Try<Nothing> > meter(
++ const std::string& context,
++ const std::string& name,
++ Owned<meters::Meter> meter);
++
++ // Sets the current value of a statistic at the current clock time
++ // or at a specified time.
++ void set(
++ const std::string& context,
++ const std::string& name,
++ double value,
++ const Time& time = Clock::now());
++
++ // Archives the provided statistic time series, and any meters associated
++ // with it. This means three things:
++ // 1. The statistic will no longer be part of the snapshot.
++ // 2. However, the time series will be retained until the window expiration.
++ // 3. All meters associated with this statistic will be removed, both
++ // (1) and (2) will apply to the metered time series as well.
++ void archive(const std::string& context, const std::string& name);
++
++ // Increments the current value of a statistic. If no statistic was
++ // previously present, an initial value of 0.0 is used.
++ void increment(const std::string& context, const std::string& name);
++
++ // Decrements the current value of a statistic. If no statistic was
++ // previously present, an initial value of 0.0 is used.
++ void decrement(const std::string& context, const std::string& name);
++
++private:
++ StatisticsProcess* process;
++};
++
++
++namespace meters {
++
++// This is the interface for statistical meters.
++// Meters provide additional metering on top of the raw statistical
++// value. Ex: Track the maximum, average, rate, etc.
++class Meter
++{
++protected:
++ Meter(const std::string& _name) : name(_name) {}
++
++public:
++ virtual ~Meter() {}
++
++ // Updates the meter with another input value.
++ // Returns the new metered value, or none if no metered value can be produced.
++ virtual Option<double> update(const Time& time, double value) = 0;
++
++ const std::string name;
++};
++
++
++// Tracks the percent of time 'used' since the last update.
++// Input values to this meter must be in seconds.
++class TimeRate : public Meter
++{
++public:
++ TimeRate(const std::string& name)
++ : Meter(name), time(None()), value(0) {}
++
++ virtual ~TimeRate() {}
++
++ virtual Option<double> update(const Time& _time, double _value)
++ {
++ Option<double> rate;
++ if (time.isSome()) {
++ rate = (_value - value) / (_time - time.get()).secs();
++ }
++
++ time = _time;
++ value = _value;
++ return rate;
++ }
++
++private:
++ Option<Time> time;
++ double value;
++};
++
++} // namespace meters {
++} // namespace process {
++
++#endif // __PROCESS_STATISTICS_HPP__
+diff --git a/include/mesos/process/time.hpp b/include/mesos/process/time.hpp
+new file mode 100644
+index 0000000..307fd2c
+--- /dev/null
++++ b/include/mesos/process/time.hpp
+@@ -0,0 +1,124 @@
++#ifndef __PROCESS_TIME_HPP__
++#define __PROCESS_TIME_HPP__
++
++#include <iomanip>
++
++#include <glog/logging.h>
++
++#include <stout/duration.hpp>
++
++namespace process {
++
++// Represents an instant in time.
++class Time
++{
++public:
++ // Constructs a time at the Epoch. It is needed because collections
++ // (e.g., std::map) require a default constructor to construct
++ // empty values.
++ Time() : sinceEpoch(Duration::zero()) {}
++
++ static Time EPOCH;
++ static Time MAX;
++
++ static Try<Time> create(double secs)
++ {
++ Try<Duration> duration = Duration::create(secs);
++ if (duration.isSome()) {
++ return Time(duration.get());
++ } else {
++ return Error("Argument too large for Time: " + duration.error());
++ }
++ }
++
++ Duration duration() const { return sinceEpoch; }
++
++ double secs() const { return sinceEpoch.secs(); }
++
++ bool operator < (const Time& t) const { return sinceEpoch < t.sinceEpoch; }
++ bool operator <= (const Time& t) const { return sinceEpoch <= t.sinceEpoch; }
++ bool operator > (const Time& t) const { return sinceEpoch > t.sinceEpoch; }
++ bool operator >= (const Time& t) const { return sinceEpoch >= t.sinceEpoch; }
++ bool operator == (const Time& t) const { return sinceEpoch == t.sinceEpoch; }
++ bool operator != (const Time& t) const { return sinceEpoch != t.sinceEpoch; }
++
++ Time& operator += (const Duration& d)
++ {
++ sinceEpoch += d;
++ return *this;
++ }
++
++ Time& operator -= (const Duration& d)
++ {
++ sinceEpoch -= d;
++ return *this;
++ }
++
++ Duration operator - (const Time& that) const
++ {
++ return sinceEpoch - that.sinceEpoch;
++ }
++
++ Time operator + (const Duration& duration) const
++ {
++ Time new_ = *this;
++ new_ += duration;
++ return new_;
++ }
++
++ Time operator - (const Duration& duration) const
++ {
++ Time new_ = *this;
++ new_ -= duration;
++ return new_;
++ }
++
++private:
++ Duration sinceEpoch;
++
++ // Made it private to avoid the confusion between Time and Duration.
++ // Users should explicitly use Clock::now() and Time::create() to
++ // create a new time instance.
++ Time(const Duration& _sinceEpoch) : sinceEpoch(_sinceEpoch) {}
++};
++
++
++// Outputs the time in RFC 3339 Format.
++inline std::ostream& operator << (std::ostream& stream, const Time& time)
++{
++ // Round down the secs to use it with strftime and then append the
++ // fraction part.
++ long secs = static_cast<long>(time.secs());
++ char date[64];
++
++ // The RFC 3339 Format.
++ tm* tm_ = gmtime(&secs);
++ if (tm_ == NULL) {
++ LOG(ERROR) << "Cannot convert the 'time' to a tm struct using gmtime(): "
++ << errno;
++ return stream;
++ }
++
++ strftime(date, 64, "%Y-%m-%d %H:%M:%S", tm_);
++ stream << date;
++
++ // Append the fraction part in nanoseconds.
++ int64_t nsecs = (time.duration() - Seconds(secs)).ns();
++
++ if (nsecs != 0) {
++ char prev = stream.fill();
++
++ // 9 digits for nanosecond level precision.
++ stream << "." << std::setfill('0') << std::setw(9) << nsecs;
++
++ // Return the stream to original formatting state.
++ stream.fill(prev);
++ }
++
++ stream << "+00:00";
++ return stream;
++}
++
++} // namespace process {
++
++#endif // __PROCESS_TIME_HPP__
+diff --git a/include/mesos/process/timeout.hpp b/include/mesos/process/timeout.hpp
+new file mode 100644
+index 0000000..4634b9f
+--- /dev/null
++++ b/include/mesos/process/timeout.hpp
+@@ -0,0 +1,84 @@
++#ifndef __PROCESS_TIMEOUT_HPP__
++#define __PROCESS_TIMEOUT_HPP__
++
++#include <process/process.hpp>
++
++#include <process/time.hpp>
++
++#include <stout/duration.hpp>
++
++
++namespace process {
++
++class Timeout
++{
++public:
++ Timeout() : timeout(Clock::now()) {}
++
++ Timeout(const Time& time) : timeout(time) {}
++
++ Timeout(const Timeout& that) : timeout(that.timeout) {}
++
++ // Constructs a Timeout instance from a Time that is the 'duration'
++ // from now.
++ static Timeout in(const Duration& duration)
++ {
++ return Timeout(Clock::now() + duration);
++ }
++
++ Timeout& operator = (const Timeout& that)
++ {
++ if (this != &that) {
++ timeout = that.timeout;
++ }
++
++ return *this;
++ }
++
++ Timeout& operator = (const Duration& duration)
++ {
++ timeout = Clock::now() + duration;
++ return *this;
++ }
++
++ bool operator == (const Timeout& that) const
++ {
++ return timeout == that.timeout;
++ }
++
++ bool operator < (const Timeout& that) const
++ {
++ return timeout < that.timeout;
++ }
++
++ bool operator <= (const Timeout& that) const
++ {
++ return timeout <= that.timeout;
++ }
++
++ // Returns the value of the timeout as a Time object.
++ Time time() const
++ {
++ return timeout;
++ }
++
++ // Returns the amount of time remaining.
++ Duration remaining() const
++ {
++ Duration remaining = timeout - Clock::now();
++ return remaining > Duration::zero() ? remaining : Duration::zero();
++ }
++
++ // Returns true if the timeout expired.
++ bool expired() const
++ {
++ return timeout <= Clock::now();
++ }
++
++private:
++ Time timeout;
++};
++
++} // namespace process {
++
++#endif // __PROCESS_TIMEOUT_HPP__
+diff --git a/include/mesos/process/timer.hpp b/include/mesos/process/timer.hpp
+new file mode 100644
+index 0000000..333a806
+--- /dev/null
++++ b/include/mesos/process/timer.hpp
+@@ -0,0 +1,76 @@
++#ifndef __PROCESS_TIMER_HPP__
++#define __PROCESS_TIMER_HPP__
++
++#include <stdlib.h> // For abort.
++
++#include <tr1/functional>
++
++#include <process/timeout.hpp>
++
++#include <stout/duration.hpp>
++
++namespace process {
++
++// Timer support!
++
++class Timer
++{
++public:
++ Timer() : id(0), pid(process::UPID()), thunk(&abort) {}
++
++ static Timer create(
++ const Duration& duration,
++ const std::tr1::function<void(void)>& thunk);
++
++ static bool cancel(const Timer& timer);
++
++ bool operator == (const Timer& that) const
++ {
++ return id == that.id;
++ }
++
++ // Invokes this timer's thunk.
++ void operator () () const
++ {
++ thunk();
++ }
++
++ // Returns the timeout associated with this timer.
++ Timeout timeout() const
++ {
++ return t;
++ }
++
++ // Returns the PID of the running process when this timer was
++ // created (via timers::create) or an empty PID if no process was
++ // running when this timer was created.
++ process::UPID creator() const
++ {
++ return pid;
++ }
++
++private:
++ Timer(long _id,
++ const Timeout& _t,
++ const process::UPID& _pid,
++ const std::tr1::function<void(void)>& _thunk)
++ : id(_id), t(_t), pid(_pid), thunk(_thunk)
++ {}
++
++ uint64_t id; // Used for equality.
++
++ Timeout t;
++
++ // We store the PID of the "issuing" (i.e., "running") process (if
++ // there is one). We don't store a pointer to the process because we
++ // can't dereference it since it might no longer be valid. (Instead,
++ // the PID can be used internally to check if the process is still
++ // valid and get a refernce to it.)
++ process::UPID pid;
++
++ std::tr1::function<void(void)> thunk;
++};
++
++} // namespace process {
++
++#endif // __PROCESS_TIMER_HPP__
+diff --git a/include/mesos/process/tuples/details.hpp b/include/mesos/process/tuples/details.hpp
+new file mode 100644
+index 0000000..34a9fb5
+--- /dev/null
++++ b/include/mesos/process/tuples/details.hpp
+@@ -0,0 +1,170 @@
++template <MSGID ID> class tuple;
++
++#undef IDENTITY
++#define IDENTITY(...) __VA_ARGS__
++
++#undef TUPLE
++#define TUPLE(ID, types) \
++ template <> class tuple<ID> : public boost::tuple<IDENTITY types> \
++ { \
++ public: \
++ tuple(const boost::tuple<IDENTITY types> &t) \
++ : boost::tuple<IDENTITY types>(t) {} \
++ \
++ tuple(const std::string &data) \
++ { \
++ std::istringstream is(data); \
++ process::tuples::deserializer d(is); \
++ deserialize(d, *this); \
++ } \
++ \
++ operator std::string () const \
++ { \
++ std::ostringstream os; \
++ process::tuples::serializer s(os); \
++ serialize(s, *this); \
++ return os.str(); \
++ } \
++ }
++
++
++inline void serialize(process::tuples::serializer &s,
++ const boost::tuples::null_type &)
++{
++}
++
++
++template <typename H, typename T>
++inline void serialize(process::tuples::serializer &s,
++ const boost::tuples::cons<H, T> &c)
++{
++ s & c.get_head();
++ serialize(s, c.get_tail());
++}
++
++
++inline void deserialize(process::tuples::deserializer &d,
++ const boost::tuples::null_type &)
++{
++}
++
++
++template <typename H, typename T>
++inline void deserialize(process::tuples::deserializer &d,
++ boost::tuples::cons<H, T> &c)
++{
++ d & c.get_head();
++ deserialize(d, c.get_tail());
++}
++
++
++template <MSGID ID>
++tuple<ID> pack()
++{
++ return tuple<ID>(::boost::make_tuple());
++}
++
++
++template <MSGID ID, typename T0>
++tuple<ID> pack(const T0 &t0)
++{
++ return tuple<ID>(::boost::make_tuple(t0));
++}
++
++
++template <MSGID ID, typename T0, typename T1>
++tuple<ID> pack(const T0 &t0, const T1 &t1)
++{
++ return tuple<ID>(::boost::make_tuple(t0, t1));
++}
++
++
++template <MSGID ID, typename T0, typename T1, typename T2>
++tuple<ID> pack(const T0 &t0, const T1 &t1, const T2 &t2)
++{
++ return tuple<ID>(::boost::make_tuple(t0, t1, t2));
++}
++
++
++template <MSGID ID,
++ typename T0, typename T1, typename T2, typename T3>
++tuple<ID> pack(const T0 &t0, const T1 &t1, const T2 &t2, const T3 &t3)
++{
++ return tuple<ID>(::boost::make_tuple(t0, t1, t2, t3));
++}
++
++
++template <MSGID ID,
++ typename T0, typename T1, typename T2, typename T3, typename T4>
++tuple<ID> pack(const T0 &t0, const T1 &t1, const T2 &t2, const T3 &t3,
++ const T4 &t4)
++{
++ return tuple<ID>(::boost::make_tuple(t0, t1, t2, t3, t4));
++}
++
++
++template <MSGID ID,
++ typename T0, typename T1, typename T2, typename T3, typename T4,
++ typename T5>
++tuple<ID> pack(const T0 &t0, const T1 &t1, const T2 &t2, const T3 &t3,
++ const T4 &t4, const T5 &t5)
++{
++ return tuple<ID>(::boost::make_tuple(t0, t1, t2, t3, t4, t5));
++}
++
++
++template <MSGID ID,
++ typename T0, typename T1, typename T2, typename T3, typename T4,
++ typename T5, typename T6>
++tuple<ID> pack(const T0 &t0, const T1 &t1, const T2 &t2, const T3 &t3,
++ const T4 &t4, const T5 &t5, const T6 &t6)
++{
++ return tuple<ID>(::boost::make_tuple(t0, t1, t2, t3, t4, t5, t6));
++}
++
++
++template <MSGID ID,
++ typename T0, typename T1, typename T2, typename T3, typename T4,
++ typename T5, typename T6, typename T7>
++tuple<ID> pack(const T0 &t0, const T1 &t1, const T2 &t2, const T3 &t3,
++ const T4 &t4, const T5 &t5, const T6 &t6, const T7 &t7)
++{
++ return tuple<ID>(::boost::make_tuple(t0, t1, t2, t3, t4, t5, t6, t7));
++}
++
++
++template <MSGID ID,
++ typename T0, typename T1, typename T2, typename T3, typename T4,
++ typename T5, typename T6, typename T7, typename T8>
++tuple<ID> pack(const T0 &t0, const T1 &t1, const T2 &t2, const T3 &t3,
++ const T4 &t4, const T5 &t5, const T6 &t6, const T7 &t7,
++ const T8 &t8)
++{
++ return tuple<ID>(::boost::make_tuple(t0, t1, t2, t3, t4, t5, t6, t7, t8));
++}
++
++
++template <MSGID ID,
++ typename T0, typename T1, typename T2, typename T3, typename T4,
++ typename T5, typename T6, typename T7, typename T8, typename T9>
++tuple<ID> pack(const T0 &t0, const T1 &t1, const T2 &t2, const T3 &t3,
++ const T4 &t4, const T5 &t5, const T6 &t6, const T7 &t7,
++ const T8 &t8, const T9 &t9)
++{
++ return tuple<ID>(::boost::make_tuple(t0, t1, t2, t3, t4, t5, t6, t7, t8, t9));
++}
++
++
++template <MSGID ID>
++tuple<ID> unpack(const std::string &data)
++{
++ return tuple<ID>(data);
++}
++
++
++template <MSGID ID, int N>
++typename boost::tuples::element<N, tuple<ID> >::type unpack(
++ const std::string &data)
++{
++ return boost::tuples::get<N>(unpack<ID>(data));
++}
+diff --git a/include/mesos/process/tuples/tuples.hpp b/include/mesos/process/tuples/tuples.hpp
+new file mode 100644
+index 0000000..16445fa
+--- /dev/null
++++ b/include/mesos/process/tuples/tuples.hpp
+@@ -0,0 +1,154 @@
++#ifndef __PROCESS_TUPLES_HPP__
++#define __PROCESS_TUPLES_HPP__
++
++#include <stdlib.h>
++
++#include <arpa/inet.h>
++
++#include <sstream>
++#include <string>
++#include <utility>
++
++#include <boost/tuple/tuple.hpp>
++
++
++namespace process { namespace tuples {
++
++// TODO(benh): Check stream errors! Report errors! Ahhhh!
++
++struct serializer
++{
++ std::ostringstream& stream;
++
++ serializer(std::ostringstream& s) : stream(s) {}
++
++ void operator & (const int32_t & i)
++ {
++ uint32_t netInt = htonl((uint32_t) i);
++ stream.write((char *) &netInt, sizeof(netInt));
++ }
++
++ void operator & (const int64_t & i)
++ {
++ uint32_t hiInt = htonl((uint32_t) (i >> 32));
++ uint32_t loInt = htonl((uint32_t) (i & 0xFFFFFFFF));
++ stream.write((char *) &hiInt, sizeof(hiInt));
++ stream.write((char *) &loInt, sizeof(loInt));
++ }
++
++#ifdef __APPLE__
++ void operator & (const intptr_t &i)
++ {
++ if (sizeof(intptr_t) == sizeof(int32_t))
++ *this & ((int32_t &) i);
++ else if (sizeof(intptr_t) == sizeof(int64_t))
++ *this & ((int64_t &) i);
++ else
++ abort();
++ }
++#endif
++
++ void operator & (const size_t &i)
++ {
++ if (sizeof(size_t) == sizeof(int32_t))
++ *this & ((int32_t &) i);
++ else if (sizeof(size_t) == sizeof(int64_t))
++ *this & ((int64_t &) i);
++ else
++ abort();
++ }
++
++ void operator & (const double &d)
++ {
++ // TODO(*): Deal with endian issues?
++ stream.write((char *) &d, sizeof(d));
++ }
++
++ void operator & (const std::string &s)
++ {
++ size_t size = s.size();
++ *this & (size);
++ stream.write(s.data(), size);
++ }
++
++ void operator & (const PID &pid)
++ {
++ *this & ((int32_t) pid.pipe);
++ *this & ((int32_t) pid.ip);
++ *this & ((int32_t) pid.port);
++ }
++};
++
++
++struct deserializer
++{
++ std::istringstream &stream;
++
++ deserializer(std::istringstream &s) : stream(s) {}
++
++ void operator & (int32_t &i)
++ {
++ uint32_t netInt;
++ stream.read((char *) &netInt, sizeof(netInt));
++ i = ntohl(netInt);
++ }
++
++ void operator & (int64_t &i)
++ {
++ uint32_t hiInt, loInt;
++ stream.read((char *) &hiInt, sizeof(hiInt));
++ stream.read((char *) &loInt, sizeof(loInt));
++ int64_t hi64 = ntohl(hiInt);
++ int64_t lo64 = ntohl(loInt);
++ i = (hi64 << 32) | lo64;
++ }
++
++#ifdef __APPLE__
++ void operator & (intptr_t &i)
++ {
++ if (sizeof(intptr_t) == sizeof(int32_t))
++ *this & ((int32_t &) i);
++ else if (sizeof(intptr_t) == sizeof(int64_t))
++ *this & ((int64_t &) i);
++ else
++ abort();
++ }
++#endif
++
++ void operator & (size_t &i)
++ {
++ if (sizeof(size_t) == sizeof(int32_t))
++ *this & ((int32_t &) i);
++ else if (sizeof(size_t) == sizeof(int64_t))
++ *this & ((int64_t &) i);
++ else
++ abort();
++ }
++
++ void operator & (double &d)
++ {
++ // TODO(*): Deal with endian issues?
++ stream.read((char *) &d, sizeof(d));
++ }
++
++ void operator & (std::string &s)
++ {
++ size_t size;
++ *this & (size);
++ s.resize(size);
++ stream.read((char *) s.data(), size);
++ }
++
++ void operator & (PID &pid)
++ {
++ *this & ((int32_t &) pid.pipe);
++ *this & ((int32_t &) pid.ip);
++ *this & ((int32_t &) pid.port);
++ }
++};
++
++
++}} // namespace process { namespace tuples {
++
++
++#endif // __PROCESS_TUPLES_HPP__
+diff --git a/include/mesos/stout/bytes.hpp b/include/mesos/stout/bytes.hpp
+new file mode 100644
+index 0000000..754fbb2
+--- /dev/null
++++ b/include/mesos/stout/bytes.hpp
+@@ -0,0 +1,160 @@
++#ifndef __STOUT_BYTES_HPP__
++#define __STOUT_BYTES_HPP__
++
++#include <ctype.h> // For 'isdigit'.
++#include <stdint.h>
++
++#include <iomanip>
++#include <iostream>
++#include <string>
++
++#include "numify.hpp"
++#include "strings.hpp"
++#include "try.hpp"
++
++
++class Bytes
++{
++public:
++ static Try<Bytes> parse(const std::string& s)
++ {
++ size_t index = 0;
++
++ while (index < s.size()) {
++ if (isdigit(s[index])) {
++ index++;
++ continue;
++ } else if (s[index] == '.') {
++ return Error("Fractional bytes '" + s + "'");
++ }
++
++ Try<uint64_t> value = numify<uint64_t>(s.substr(0, index));
++
++ if (value.isError()) {
++ return Error(value.error());
++ }
++
++ const std::string& unit = strings::upper(s.substr(index));
++
++ if (unit == "B") {
++ return Bytes(value.get(), BYTES);
++ } else if (unit == "KB") {
++ return Bytes(value.get(), KILOBYTES);
++ } else if (unit == "MB") {
++ return Bytes(value.get(), MEGABYTES);
++ } else if (unit == "GB") {
++ return Bytes(value.get(), GIGABYTES);
++ } else if (unit == "TB") {
++ return Bytes(value.get(), TERABYTES);
++ } else {
++ return Error("Unknown bytes unit '" + unit + "'");
++ }
++ }
++ return Error("Invalid bytes '" + s + "'");
++ }
++
++ Bytes(uint64_t bytes = 0) : value(bytes) {}
++ Bytes(uint64_t _value, uint64_t _unit) : value(_value * _unit) {}
++
++ // TODO(bmahler): Consider killing kilobytes to terabyte helpers, given
++ // they implicitly lose precision if not careful.
++ uint64_t bytes() const { return value; }
++ uint64_t kilobytes() const { return value / KILOBYTES; }
++ uint64_t megabytes() const { return value / MEGABYTES; }
++ uint64_t gigabytes() const { return value / GIGABYTES; }
++ uint64_t terabytes() const { return value / TERABYTES; }
++
++ bool operator < (const Bytes& that) const { return value < that.value; }
++ bool operator <= (const Bytes& that) const { return value <= that.value; }
++ bool operator > (const Bytes& that) const { return value > that.value; }
++ bool operator >= (const Bytes& that) const { return value >= that.value; }
++ bool operator == (const Bytes& that) const { return value == that.value; }
++ bool operator != (const Bytes& that) const { return value != that.value; }
++
++ Bytes& operator += (const Bytes& that)
++ {
++ value += that.value;
++ return *this;
++ }
++
++ Bytes& operator -= (const Bytes& that)
++ {
++ value -= that.value;
++ return *this;
++ }
++
++protected:
++ static const uint64_t BYTES = 1;
++ static const uint64_t KILOBYTES = 1024 * BYTES;
++ static const uint64_t MEGABYTES = 1024 * KILOBYTES;
++ static const uint64_t GIGABYTES = 1024 * MEGABYTES;
++ static const uint64_t TERABYTES = 1024 * GIGABYTES;
++
++private:
++ uint64_t value;
++};
++
++
++class Kilobytes : public Bytes
++{
++public:
++ explicit Kilobytes(uint64_t value) : Bytes(value, KILOBYTES) {}
++};
++
++
++class Megabytes : public Bytes
++{
++public:
++ explicit Megabytes(uint64_t value) : Bytes(value, MEGABYTES) {}
++};
++
++
++class Gigabytes : public Bytes
++{
++public:
++ explicit Gigabytes(uint64_t value) : Bytes(value, GIGABYTES) {}
++};
++
++
++class Terabytes : public Bytes
++{
++public:
++ explicit Terabytes(uint64_t value) : Bytes(value, TERABYTES) {}
++};
++
++
++inline std::ostream& operator << (std::ostream& stream, const Bytes& bytes)
++{
++ // Only raise the unit when there is no loss of information.
++ if (bytes.bytes() == 0) {
++ return stream << bytes.bytes() << "B";
++ } else if (bytes.bytes() % 1024 != 0) {
++ return stream << bytes.bytes() << "B";
++ } else if (bytes.kilobytes() % 1024 != 0) {
++ return stream << bytes.kilobytes() << "KB";
++ } else if (bytes.megabytes() % 1024 != 0) {
++ return stream << bytes.megabytes() << "MB";
++ } else if (bytes.gigabytes() % 1024 != 0) {
++ return stream << bytes.gigabytes() << "GB";
++ } else {
++ return stream << bytes.terabytes() << "TB";
++ }
++}
++
++
++inline Bytes operator + (const Bytes& lhs, const Bytes& rhs)
++{
++ Bytes sum = lhs;
++ sum += rhs;
++ return sum;
++}
++
++
++inline Bytes operator - (const Bytes& lhs, const Bytes& rhs)
++{
++ Bytes diff = lhs;
++ diff -= rhs;
++ return diff;
++}
++
++#endif // __STOUT_BYTES_HPP__
+diff --git a/include/mesos/stout/cache.hpp b/include/mesos/stout/cache.hpp
+new file mode 100644
+index 0000000..653507c
+--- /dev/null
++++ b/include/mesos/stout/cache.hpp
+@@ -0,0 +1,131 @@
++#ifndef __STOUT_CACHE_HPP__
++#define __STOUT_CACHE_HPP__
++
++#include <functional>
++#include <iostream>
++#include <list>
++#include <map>
++
++#include <tr1/functional>
++#include <tr1/unordered_map>
++
++#include "none.hpp"
++#include "option.hpp"
++
++// Forward declaration.
++template <typename Key, typename Value>
++class cache;
++
++// Outputs the key/value pairs from least to most-recently used.
++template <typename Key, typename Value>
++std::ostream& operator << (
++ std::ostream& stream,
++ const cache<Key, Value>& c);
++
++
++// Provides a least-recently used (LRU) cache of some predefined
++// capacity. A "write" and a "read" both count as uses.
++template <typename Key, typename Value>
++class cache
++{
++public:
++ typedef std::list<Key> list;
++ typedef std::tr1::unordered_map<
++ Key, std::pair<Value, typename list::iterator> > map;
++
++ explicit cache(int _capacity) : capacity(_capacity) {}
++
++ void put(const Key& key, const Value& value)
++ {
++ typename map::iterator i = values.find(key);
++ if (i == values.end()) {
++ insert(key, value);
++ } else {
++ (*i).second.first = value;
++ use(i);
++ }
++ }
++
++ Option<Value> get(const Key& key)
++ {
++ typename map::iterator i = values.find(key);
++
++ if (i != values.end()) {
++ use(i);
++ return (*i).second.first;
++ }
++
++ return None();
++ }
++
++private:
++ // Not copyable, not assignable.
++ cache(const cache&);
++ cache& operator = (const cache&);
++
++ // Give the operator access to our internals.
++ friend std::ostream& operator << <>(
++ std::ostream& stream,
++ const cache<Key, Value>& c);
++
++ // Insert key/value into the cache.
++ void insert(const Key& key, const Value& value)
++ {
++ if (keys.size() == capacity) {
++ evict();
++ }
++
++ // Get a "pointer" into the lru list for efficient update.
++ typename list::iterator i = keys.insert(keys.end(), key);
++
++ // Save key/value and "pointer" into lru list.
++ values.insert(std::make_pair(key, std::make_pair(value, i)));
++ }
++
++ // Updates the LRU ordering in the cache for the given iterator.
++ void use(const typename map::iterator& i)
++ {
++ // Move the "pointer" to the end of the lru list.
++ keys.splice(keys.end(), keys, (*i).second.second);
++
++ // Now update the "pointer" so we can do this again.
++ (*i).second.second = --keys.end();
++ }
++
++ // Evict the least-recently used element from the cache.
++ void evict()
++ {
++ const typename map::iterator& i = values.find(keys.front());
++ CHECK(i != values.end());
++ values.erase(i);
++ keys.pop_front();
++ }
++
++ // Size of the cache.
++ int capacity;
++
++ // Cache of values and "pointers" into the least-recently used list.
++ map values;
++
++ // Keys ordered by least-recently used.
++ list keys;
++};
++
++
++template <typename Key, typename Value>
++std::ostream& operator << (
++ std::ostream& stream,
++ const cache<Key, Value>& c)
++{
++ typename cache<Key, Value>::list::const_iterator i1;
++ for (i1 = c.keys.begin(); i1 != c.keys.end(); i1++) {
++ stream << *i1 << ": ";
++ typename cache<Key, Value>::map::const_iterator i2;
++ i2 = c.values.find(*i1);
++ CHECK(i2 != c.values.end());
++ stream << *i2 << std::endl;
++ }
++ return stream;
++}
++
++#endif // __STOUT_CACHE_HPP__
+diff --git a/include/mesos/stout/check.hpp b/include/mesos/stout/check.hpp
+new file mode 100644
+index 0000000..eb31841
+--- /dev/null
++++ b/include/mesos/stout/check.hpp
+@@ -0,0 +1,90 @@
++#ifndef __STOUT_CHECK_HPP__
++#define __STOUT_CHECK_HPP__
++
++#include <ostream>
++#include <sstream>
++#include <string>
++
++#include <glog/logging.h> // Includes LOG(*), PLOG(*), CHECK, etc.
++
++#include <stout/none.hpp>
++#include <stout/option.hpp>
++#include <stout/result.hpp>
++#include <stout/some.hpp>
++#include <stout/try.hpp>
++
++// Provides a CHECK_SOME macro, akin to CHECK.
++// This appends the error if possible to the end of the log message, so there's
++// no need to append the error message explicitly.
++#define CHECK_SOME(expression) \
++ for (const Option<std::string>& _error = _check(expression); \
++ _error.isSome();) \
++ _CheckSome(__FILE__, __LINE__, #expression, _error.get()).stream() \
++
++// Private structs/functions used for CHECK_SOME.
++
++template <typename T>
++Option<std::string> _check(const Option<T>& o)
++{
++ if (o.isNone()) {
++ return Some("is NONE");
++ }
++ return None();
++}
++
++
++template <typename T>
++Option<std::string> _check(const Try<T>& t)
++{
++ if (t.isError()) {
++ return t.error();
++ }
++ return None();
++}
++
++
++template <typename T>
++Option<std::string> _check(const Result<T>& r)
++{
++ if (r.isError()) {
++ return r.error();
++ } else if (r.isNone()) {
++ return Some("is NONE");
++ }
++ return None();
++}
++
++
++struct _CheckSome
++{
++ _CheckSome(const char* _file,
++ int _line,
++ const char* _expression,
++ const std::string& _error)
++ : file(_file),
++ line(_line),
++ expression(_expression),
++ error(_error)
++ {
++ out << "CHECK_SOME(" << expression << "): ";
++ }
++
++ ~_CheckSome()
++ {
++ out << error;
++ google::LogMessageFatal(file.c_str(), line).stream() << out.str();
++ }
++
++ std::ostream& stream()
++ {
++ return out;
++ }
++
++ const std::string file;
++ const int line;
++ const std::string expression;
++ const std::string error;
++ std::ostringstream out;
++};
++
++#endif // __STOUT_CHECK_HPP__
+diff --git a/include/mesos/stout/duration.hpp b/include/mesos/stout/duration.hpp
+new file mode 100644
+index 0000000..2f5a93e
+--- /dev/null
++++ b/include/mesos/stout/duration.hpp
+@@ -0,0 +1,357 @@
++#ifndef __STOUT_DURATION_HPP__
++#define __STOUT_DURATION_HPP__
++
++#include <ctype.h> // For 'isdigit'.
++#include <limits.h> // For 'LLONG_(MAX|MIN)'
++
++#include <iomanip>
++#include <iostream>
++#include <string>
++
++#include "error.hpp"
++#include "numify.hpp"
++#include "try.hpp"
++
++class Duration
++{
++public:
++ static Try<Duration> parse(const std::string& s)
++ {
++ // TODO(benh): Support negative durations (i.e., starts with '-').
++ size_t index = 0;
++ while (index < s.size()) {
++ if (isdigit(s[index]) || s[index] == '.') {
++ index++;
++ continue;
++ }
++
++ Try<double> value = numify<double>(s.substr(0, index));
++
++ if (value.isError()) {
++ return Error(value.error());
++ }
++
++ const std::string& unit = s.substr(index);
++
++ if (unit == "ns") {
++ return Duration(value.get(), NANOSECONDS);
++ } else if (unit == "us") {
++ return Duration(value.get(), MICROSECONDS);
++ } else if (unit == "ms") {
++ return Duration(value.get(), MILLISECONDS);
++ } else if (unit == "secs") {
++ return Duration(value.get(), SECONDS);
++ } else if (unit == "mins") {
++ return Duration(value.get(), MINUTES);
++ } else if (unit == "hrs") {
++ return Duration(value.get(), HOURS);
++ } else if (unit == "days") {
++ return Duration(value.get(), DAYS);
++ } else if (unit == "weeks") {
++ return Duration(value.get(), WEEKS);
++ } else {
++ return Error("Unknown duration unit '" + unit + "'");
++ }
++ }
++ return Error("Invalid duration '" + s + "'");
++ }
++
++ static Try<Duration> create(double seconds);
++
++ Duration() : nanos(0) {}
++
++ int64_t ns() const { return nanos; }
++ double us() const { return static_cast<double>(nanos) / MICROSECONDS; }
++ double ms() const { return static_cast<double>(nanos) / MILLISECONDS; }
++ double secs() const { return static_cast<double>(nanos) / SECONDS; }
++ double mins() const { return static_cast<double>(nanos) / MINUTES; }
++ double hrs() const { return static_cast<double>(nanos) / HOURS; }
++ double days() const { return static_cast<double>(nanos) / DAYS; }
++ double weeks() const { return static_cast<double>(nanos) / WEEKS; }
++
++ bool operator < (const Duration& d) const { return nanos < d.nanos; }
++ bool operator <= (const Duration& d) const { return nanos <= d.nanos; }
++ bool operator > (const Duration& d) const { return nanos > d.nanos; }
++ bool operator >= (const Duration& d) const { return nanos >= d.nanos; }
++ bool operator == (const Duration& d) const { return nanos == d.nanos; }
++ bool operator != (const Duration& d) const { return nanos != d.nanos; }
++
++ Duration& operator += (const Duration& that)
++ {
++ nanos += that.nanos;
++ return *this;
++ }
++
++ Duration& operator -= (const Duration& that)
++ {
++ nanos -= that.nanos;
++ return *this;
++ }
++
++ Duration& operator *= (double multiplier)
++ {
++ nanos = static_cast<int64_t>(nanos * multiplier);
++ return *this;
++ }
++
++ Duration& operator /= (double divisor)
++ {
++ nanos = static_cast<int64_t>(nanos / divisor);
++ return *this;
++ }
++
++ Duration operator + (const Duration& that) const
++ {
++ Duration sum = *this;
++ sum += that;
++ return sum;
++ }
++
++ Duration operator - (const Duration& that) const
++ {
++ Duration diff = *this;
++ diff -= that;
++ return diff;
++ }
++
++ Duration operator * (double multiplier) const
++ {
++ Duration product = *this;
++ product *= multiplier;
++ return product;
++ }
++
++ Duration operator / (double divisor) const
++ {
++ Duration quotient = *this;
++ quotient /= divisor;
++ return quotient;
++ }
++
++ // TODO(xujyan): Use constexpr for the following variables after
++ // switching to C++11.
++ // A constant holding the maximum value a Duration can have.
++ static Duration max();
++ // A constant holding the minimum (negative) value a Duration can
++ // have.
++ static Duration min();
++ // A constant holding a Duration of a "zero" value.
++ static Duration zero() { return Duration(); }
++
++protected:
++ static const int64_t NANOSECONDS = 1;
++ static const int64_t MICROSECONDS = 1000 * NANOSECONDS;
++ static const int64_t MILLISECONDS = 1000 * MICROSECONDS;
++ static const int64_t SECONDS = 1000 * MILLISECONDS;
++ static const int64_t MINUTES = 60 * SECONDS;
++ static const int64_t HOURS = 60 * MINUTES;
++ static const int64_t DAYS = 24 * HOURS;
++ static const int64_t WEEKS = 7 * DAYS;
++
++ // For the Seconds, Minutes, Hours, Days & Weeks constructor.
++ Duration(int32_t value, int64_t unit)
++ : nanos(value * unit) {}
++
++ // For the Nanoseconds, Microseconds, Milliseconds constructor.
++ Duration(int64_t value, int64_t unit)
++ : nanos(value * unit) {}
++
++private:
++ // Used only by "parse".
++ Duration(double value, int64_t unit)
++ : nanos(static_cast<int64_t>(value * unit)) {}
++
++ int64_t nanos;
++
++ friend std::ostream& operator << (
++ std::ostream& stream,
++ const Duration& duration);
++};
++
++
++class Nanoseconds : public Duration
++{
++public:
++ explicit Nanoseconds(int64_t nanoseconds)
++ : Duration(nanoseconds, NANOSECONDS) {}
++
++ Nanoseconds(const Duration& d) : Duration(d) {}
++};
++
++
++class Microseconds : public Duration
++{
++public:
++ explicit Microseconds(int64_t microseconds)
++ : Duration(microseconds, MICROSECONDS) {}
++
++ Microseconds(const Duration& d) : Duration(d) {}
++};
++
++
++class Milliseconds : public Duration
++{
++public:
++ explicit Milliseconds(int64_t milliseconds)
++ : Duration(milliseconds, MILLISECONDS) {}
++
++ Milliseconds(const Duration& d) : Duration(d) {}
++};
++
++
++class Seconds : public Duration
++{
++public:
++ explicit Seconds(int64_t seconds)
++ : Duration(seconds, SECONDS) {}
++
++ Seconds(const Duration& d) : Duration(d) {}
++};
++
++
++class Minutes : public Duration
++{
++public:
++ explicit Minutes(int32_t minutes)
++ : Duration(minutes, MINUTES) {}
++
++ Minutes(const Duration& d) : Duration(d) {}
++};
++
++
++class Hours : public Duration
++{
++public:
++ explicit Hours(int32_t hours)
++ : Duration(hours, HOURS) {}
++
++ Hours(const Duration& d) : Duration(d) {}
++};
++
++
++class Days : public Duration
++{
++public:
++ explicit Days(int32_t days)
++ : Duration(days, DAYS) {}
++
++ Days(const Duration& d) : Duration(d) {}
++};
++
++
++class Weeks : public Duration
++{
++public:
++ explicit Weeks(int32_t value) : Duration(value, WEEKS) {}
++
++ Weeks(const Duration& d) : Duration(d) {}
++};
++
++
++inline std::ostream& operator << (
++ std::ostream& stream,
++ const Duration& duration_)
++{
++ long precision = stream.precision();
++
++ // Output the duration in full double precision.
++ stream.precision(std::numeric_limits<double>::digits10);
++
++ // Parse the duration as the sign and the absolute value.
++ Duration duration = duration_;
++ if (duration_ < Duration::zero()) {
++ stream << "-";
++
++ // Duration::min() may not be representable as a positive Duration.
++ if (duration_ == Duration::min()) {
++ duration = Duration::max();
++ } else {
++ duration = duration_ * -1;
++ }
++ }
++
++ // First determine which bucket of time unit the duration falls into
++ // then check whether the duration can be represented as a whole
++ // number with this time unit or a smaller one.
++ // e.g. 1.42857142857143weeks falls into the 'Weeks' bucket but
++ // reads better with a smaller unit: '10days'. So we use 'days'
++ // instead of 'weeks' to output the duration.
++ int64_t nanoseconds = duration.ns();
++ if (duration < Microseconds(1)) {
++ stream << duration.ns() << "ns";
++ } else if (duration < Milliseconds(1)) {
++ if (nanoseconds % Duration::MICROSECONDS != 0) {
++ // We can't get a whole number using this unit but we can at
++ // one level down.
++ stream << duration.ns() << "ns";
++ } else {
++ stream << duration.us() << "us";
++ }
++ } else if (duration < Seconds(1)) {
++ if (nanoseconds % Duration::MILLISECONDS != 0 &&
++ nanoseconds % Duration::MICROSECONDS == 0) {
++ stream << duration.us() << "us";
++ } else {
++ stream << duration.ms() << "ms";
++ }
++ } else if (duration < Minutes(1)) {
++ if (nanoseconds % Duration::SECONDS != 0 &&
++ nanoseconds % Duration::MILLISECONDS == 0) {
++ stream << duration.ms() << "ms";
++ } else {
++ stream << duration.secs() << "secs";
++ }
++ } else if (duration < Hours(1)) {
++ if (nanoseconds % Duration::MINUTES != 0 &&
++ nanoseconds % Duration::SECONDS == 0) {
++ stream << duration.secs() << "secs";
++ } else {
++ stream << duration.mins() << "mins";
++ }
++ } else if (duration < Days(1)) {
++ if (nanoseconds % Duration::HOURS != 0 &&
++ nanoseconds % Duration::MINUTES == 0) {
++ stream << duration.mins() << "mins";
++ } else {
++ stream << duration.hrs() << "hrs";
++ }
++ } else if (duration < Weeks(1)) {
++ if (nanoseconds % Duration::DAYS != 0 &&
++ nanoseconds % Duration::HOURS == 0) {
++ stream << duration.hrs() << "hrs";
++ } else {
++ stream << duration.days() << "days";
++ }
++ } else {
++ if (nanoseconds % Duration::WEEKS != 0 &&
++ nanoseconds % Duration::DAYS == 0) {
++ stream << duration.days() << "days";
++ } else {
++ stream << duration.weeks() << "weeks";
++ }
++ }
++
++ // Return the stream to original formatting state.
++ stream.precision(precision);
++
++ return stream;
++}
++
++
++inline Try<Duration> Duration::create(double seconds)
++{
++ if (seconds * SECONDS > LLONG_MAX || seconds * SECONDS < LLONG_MIN) {
++ return Error("Argument out of the range that a Duration can represent due "
++ "to int64_t's size limit");
++ }
++
++ return Nanoseconds(static_cast<int64_t>(seconds * SECONDS));
++}
++
++
++inline Duration Duration::max() { return Nanoseconds(LLONG_MAX); }
++
++
++inline Duration Duration::min() { return Nanoseconds(LLONG_MIN); }
++
++#endif // __STOUT_DURATION_HPP__
+diff --git a/include/mesos/stout/error.hpp b/include/mesos/stout/error.hpp
+new file mode 100644
+index 0000000..97a5cec
+--- /dev/null
++++ b/include/mesos/stout/error.hpp
+@@ -0,0 +1,72 @@
++#ifndef __STOUT_ERROR_HPP__
++#define __STOUT_ERROR_HPP__
++
++#include <errno.h>
++#include <string.h> // For strerror.
++
++#include <string>
++
++#include "result.hpp"
++#include "try.hpp"
++
++// An "error" type that is implicitly convertible to a Try<T> or
++// Result<T> for any T (effectively "syntactic sugar" to make code
++// more readable). The implementation uses cast operators to perform
++// the conversions instead of adding constructors to Try/Result
++// directly. One could imagine revisiting that decision for C++11
++// because the use of rvalue reference could eliminate some
++// unnecessary copies. However, performance is not critical since
++// Error should not get called very often in practice (if so, it's
++// probably being used for things that aren't really errors or there
++// is a more serious problem during execution).
++
++class Error
++{
++public:
++ explicit Error(const std::string& _message) : message(_message) {}
++
++ template <typename T>
++ operator Try<T> () const
++ {
++ return Try<T>::error(message);
++ }
++
++ // Give the compiler some help for nested Try<T>. For example,
++ // enable converting Error to an Option<Try<T>>. Note that this will
++ // bind to the innermost Try<T>.
++ template <template <typename> class S, typename T>
++ operator S<Try<T> > () const
++ {
++ return S<Try<T> >(Try<T>::error(message));
++ }
++
++ template <typename T>
++ operator Result<T> () const
++ {
++ return Result<T>::error(message);
++ }
++
++ // Give the compiler some help for nested Result<T>. For example,
++ // enable converting Error to an Option<Result<T>>. Note that this
++ // will bind to the innermost Result<T>.
++ template <template <typename> class S, typename T>
++ operator S<Result<T> > () const
++ {
++ return S<Result<T> >(Result<T>::error(message));
++ }
++
++ const std::string message;
++};
++
++
++class ErrnoError : public Error
++{
++public:
++ ErrnoError()
++ : Error(std::string(strerror(errno))) {}
++
++ ErrnoError(const std::string& message)
++ : Error(message + ": " + std::string(strerror(errno))) {}
++};
++
++#endif // __STOUT_ERROR_HPP__
+diff --git a/include/mesos/stout/exit.hpp b/include/mesos/stout/exit.hpp
+new file mode 100644
+index 0000000..e8da726
+--- /dev/null
++++ b/include/mesos/stout/exit.hpp
+@@ -0,0 +1,37 @@
++#ifndef __STOUT_EXIT_HPP__
++#define __STOUT_EXIT_HPP__
++
++#include <stdlib.h>
++
++#include <iostream> // For std::cerr.
++#include <ostream>
++#include <sstream>
++#include <string>
++
++// Exit takes an exit status and provides a stream for output prior to
++// exiting. This is like glog's LOG(FATAL) or CHECK, except that it
++// does _not_ print a stack trace.
++//
++// Ex: EXIT(1) << "Cgroups are not present in this system.";
++#define EXIT(status) __Exit(status).stream()
++
++struct __Exit
++{
++ __Exit(int _status) : status(_status) {}
++
++ ~__Exit()
++ {
++ std::cerr << out.str() << std::endl;
++ exit(status);
++ }
++
++ std::ostream& stream()
++ {
++ return out;
++ }
++
++ std::ostringstream out;
++ const int status;
++};
++
++#endif // __STOUT_EXIT_HPP__
+diff --git a/include/mesos/stout/fatal.hpp b/include/mesos/stout/fatal.hpp
+new file mode 100644
+index 0000000..eabee3e
+--- /dev/null
++++ b/include/mesos/stout/fatal.hpp
+@@ -0,0 +1,43 @@
++#ifndef __STOUT_FATAL_HPP__
++#define __STOUT_FATAL_HPP__
++
++#include <stdarg.h>
++#include <stdio.h>
++#include <stdlib.h>
++
++
++/*
++ * Like the non-debug version except includes the file name and line
++ * number in the output.
++ */
++#define fatal(fmt...) __fatal(__FILE__, __LINE__, fmt)
++inline void __fatal(const char *file, int line, const char *fmt, ...)
++{
++ va_list args;
++ va_start(args, fmt);
++ vfprintf(stderr, fmt, args);
++ fprintf(stderr, " (%s:%u)\n", file, line);
++ fflush(stderr);
++ va_end(args);
++ exit(1);
++}
++
++
++/*
++ * Like the non-debug version except includes the file name and line
++ * number in the output.
++ */
++#define fatalerror(fmt...) __fatalerror(__FILE__, __LINE__, fmt)
++inline void __fatalerror(const char *file, int line, const char *fmt, ...)
++{
++ va_list args;
++ va_start(args, fmt);
++ vfprintf(stderr, fmt, args);
++ fprintf(stderr, " (%s:%u): ", file, line);
++ perror(NULL);
++ fflush(stderr);
++ va_end(args);
++ exit(1);
++}
++
++#endif // __STOUT_FATAL_HPP__
+diff --git a/include/mesos/stout/flags.hpp b/include/mesos/stout/flags.hpp
+new file mode 100644
+index 0000000..a70db19
+--- /dev/null
++++ b/include/mesos/stout/flags.hpp
+@@ -0,0 +1,70 @@
++#ifndef __STOUT_FLAGS_HPP__
++#define __STOUT_FLAGS_HPP__
++
++#include <stout/flags/flags.hpp>
++
++// An abstraction for application/library "flags". An example is
++// probably best:
++// -------------------------------------------------------------
++// class MyFlags : public virtual FlagsBase // Use 'virtual' for composition!
++// {
++// public:
++// Flags()
++// {
++// add(&debug,
++// "debug",
++// "Help string for debug",
++// false);
++//
++// add(&name,
++// "name",
++// "Help string for name");
++// }
++
++// bool debug;
++// Option<string> name;
++// };
++//
++// ...
++//
++// map<string, Option<string> > values;
++// values["no-debug"] = None(); // --no-debug
++// values["debug"] = None(); // --debug
++// values["debug"] = Some("true"); // --debug=true
++// values["debug"] = Some("false"); // --debug=false
++// values["name"] = Some("frank"); // --name=frank
++//
++// MyFlags flags;
++// flags.load(values);
++// flags.name.isSome() ...
++// flags.debug ...
++// -------------------------------------------------------------
++//
++// You can also compose flags provided that each has used "virtual
++// inheritance":
++// -------------------------------------------------------------
++// Flags<MyFlags1, MyFlags2> flags;
++// flags.add(...); // Any other flags you want to throw in there.
++// flags.load(values);
++// flags.flag_from_myflags1 ...
++// flags.flag_from_myflags2 ...
++// -------------------------------------------------------------
++//
++// "Fail early, fail often":
++//
++// You can not add duplicate flags, this is checked for you at compile
++// time for composite flags (e.g., Flag<MyFlags1, MyFlags2>) and also
++// checked at runtime for any other flags added via inheritance or
++// Flags::add(...).
++//
++// Flags that can not be loaded (e.g., attempting to use the 'no-'
++// prefix for a flag that is not boolean) will print a message to
++// standard error and abort the process.
++
++// TODO(benh): Provide a boolean which specifies whether or not to
++// abort on duplicates or load errors.
++
++// TODO(benh): Make prefix for environment variables configurable
++// (e.g., "MESOS_").
++
++#endif // __STOUT_FLAGS_HPP__
+diff --git a/include/mesos/stout/flags/flag.hpp b/include/mesos/stout/flags/flag.hpp
+new file mode 100644
+index 0000000..d31c984
+--- /dev/null
++++ b/include/mesos/stout/flags/flag.hpp
+@@ -0,0 +1,26 @@
++#ifndef __STOUT_FLAGS_FLAG_HPP__
++#define __STOUT_FLAGS_FLAG_HPP__
++
++#include <string>
++
++#include <tr1/functional>
++
++#include <stout/nothing.hpp>
++#include <stout/try.hpp>
++
++namespace flags {
++
++// Forward declaration.
++class FlagsBase;
++
++struct Flag
++{
++ std::string name;
++ std::string help;
++ bool boolean;
++ std::tr1::function<Try<Nothing>(FlagsBase*, const std::string&)> loader;
++};
++
++} // namespace flags {
++
++#endif // __STOUT_FLAGS_FLAG_HPP__
+diff --git a/include/mesos/stout/flags/flags.hpp b/include/mesos/stout/flags/flags.hpp
+new file mode 100644
+index 0000000..cfe996e
+--- /dev/null
++++ b/include/mesos/stout/flags/flags.hpp
+@@ -0,0 +1,488 @@
++#ifndef __STOUT_FLAGS_FLAGS_HPP__
++#define __STOUT_FLAGS_FLAGS_HPP__
++
++#include <stdlib.h> // For abort.
++
++#include <map>
++#include <string>
++#include <typeinfo> // For typeid.
++
++#include <tr1/functional>
++
++#include <stout/error.hpp>
++#include <stout/exit.hpp>
++#include <stout/foreach.hpp>
++#include <stout/none.hpp>
++#include <stout/nothing.hpp>
++#include <stout/option.hpp>
++#include <stout/os.hpp>
++#include <stout/some.hpp>
++#include <stout/stringify.hpp>
++#include <stout/strings.hpp>
++#include <stout/try.hpp>
++
++#include <stout/flags/flag.hpp>
++#include <stout/flags/loader.hpp>
++#include <stout/flags/parse.hpp>
++
++namespace flags {
++
++class FlagsBase
++{
++public:
++ virtual ~FlagsBase() {}
++
++ // Load any flags from the environment given the variable prefix,
++ // i.e., given prefix 'STOUT_' will load a flag named 'foo' via
++ // environment variables 'STOUT_foo' or 'STOUT_FOO'.
++ virtual Try<Nothing> load(const std::string& prefix);
++
++ // Load any flags from the environment given the variable prefix
++ // (see above) followed by loading from the command line (via 'argc'
++ // and 'argv'). If 'unknowns' is true then we'll ignore unknown
++ // flags we see while loading. If 'duplicates' is true then we'll
++ // ignore any duplicates we see while loading.
++ virtual Try<Nothing> load(
++ const Option<std::string>& prefix,
++ int argc,
++ char** argv,
++ bool unknowns = false,
++ bool duplicates = false);
++
++ Try<Nothing> load(
++ const std::string& prefix,
++ int argc,
++ char** argv,
++ bool unknowns = false,
++ bool duplicates = false);
++
++ virtual Try<Nothing> load(
++ const std::map<std::string, Option<std::string> >& values,
++ bool unknowns = false);
++
++ virtual Try<Nothing> load(
++ const std::map<std::string, std::string>& values,
++ bool unknowns = false);
++
++ // Returns a string describing the flags.
++ std::string usage() const;
++
++ typedef std::map<std::string, Flag>::const_iterator const_iterator;
++
++ const_iterator begin() const { return flags.begin(); }
++ const_iterator end() const { return flags.end(); }
++
++ template <typename T1, typename T2>
++ void add(T1* t1,
++ const std::string& name,
++ const std::string& help,
++ const T2& t2);
++
++ template <typename T>
++ void add(Option<T>* option,
++ const std::string& name,
++ const std::string& help);
++
++protected:
++ template <typename Flags, typename T1, typename T2>
++ void add(T1 Flags::*t1,
++ const std::string& name,
++ const std::string& help,
++ const T2& t2);
++
++ template <typename Flags, typename T>
++ void add(Option<T> Flags::*option,
++ const std::string& name,
++ const std::string& help);
++
++ void add(const Flag& flag);
++
++private:
++ // Extract environment variable "flags" with the specified prefix.
++ std::map<std::string, Option<std::string> > extract(
++ const std::string& prefix);
++
++ std::map<std::string, Flag> flags;
++};
++
++
++// Need to declare/define some explicit subclasses of FlagsBase so
++// that we can overload the 'Flags::operator FlagsN () const'
++// functions for each possible type.
++class _Flags1 : public virtual FlagsBase {};
++class _Flags2 : public virtual FlagsBase {};
++class _Flags3 : public virtual FlagsBase {};
++class _Flags4 : public virtual FlagsBase {};
++class _Flags5 : public virtual FlagsBase {};
++
++
++// TODO(benh): Add some "type constraints" for template paramters to
++// make sure they are all of type FlagsBase.
++template <typename Flags1 = _Flags1,
++ typename Flags2 = _Flags2,
++ typename Flags3 = _Flags3,
++ typename Flags4 = _Flags4,
++ typename Flags5 = _Flags5>
++class Flags : public virtual Flags1,
++ public virtual Flags2,
++ public virtual Flags3,
++ public virtual Flags4,
++ public virtual Flags5 {};
++
++
++template <typename T1, typename T2>
++void FlagsBase::add(
++ T1* t1,
++ const std::string& name,
++ const std::string& help,
++ const T2& t2)
++{
++ *t1 = t2; // Set the default.
++
++ Flag flag;
++ flag.name = name;
++ flag.help = help;
++ flag.boolean = typeid(T1) == typeid(bool);
++ flag.loader = std::tr1::bind(
++ &Loader<T1>::load,
++ t1,
++ std::tr1::function<Try<T1>(const std::string&)>(
++ std::tr1::bind(&parse<T1>, std::tr1::placeholders::_1)),
++ name,
++ std::tr1::placeholders::_2); // Use _2 because ignore FlagsBase*.
++
++ // Update the help string to include the default value.
++ flag.help += help.size() > 0 && help.find_last_of("\n\r") != help.size() - 1
++ ? " (default: " // On same line, add space.
++ : "(default: "; // On newline.
++ flag.help += stringify(t2);
++ flag.help += ")";
++
++ FlagsBase::add(flag);
++}
++
++
++template <typename T>
++void FlagsBase::add(
++ Option<T>* option,
++ const std::string& name,
++ const std::string& help)
++{
++ Flag flag;
++ flag.name = name;
++ flag.help = help;
++ flag.boolean = typeid(T) == typeid(bool);
++ flag.loader = std::tr1::bind(
++ &OptionLoader<T>::load,
++ option,
++ std::tr1::function<Try<T>(const std::string&)>(
++ std::tr1::bind(&parse<T>, std::tr1::placeholders::_1)),
++ name,
++ std::tr1::placeholders::_2); // Use _2 because ignore FlagsBase*.
++
++ FlagsBase::add(flag);
++}
++
++
++template <typename Flags, typename T1, typename T2>
++void FlagsBase::add(
++ T1 Flags::*t1,
++ const std::string& name,
++ const std::string& help,
++ const T2& t2)
++{
++ Flags* flags = dynamic_cast<Flags*>(this);
++ if (flags == NULL) {
++ std::cerr << "Attempted to add flag '" << name
++ << "' with incompatible type" << std::endl;
++ abort();
++ } else {
++ flags->*t1 = t2; // Set the default.
++ }
++
++ Flag flag;
++ flag.name = name;
++ flag.help = help;
++ flag.boolean = typeid(T1) == typeid(bool);
++ flag.loader = std::tr1::bind(
++ &MemberLoader<Flags, T1>::load,
++ std::tr1::placeholders::_1,
++ t1,
++ std::tr1::function<Try<T1>(const std::string&)>(
++ std::tr1::bind(&parse<T1>, std::tr1::placeholders::_1)),
++ name,
++ std::tr1::placeholders::_2);
++
++ // Update the help string to include the default value.
++ flag.help += help.size() > 0 && help.find_last_of("\n\r") != help.size() - 1
++ ? " (default: " // On same line, add space.
++ : "(default: "; // On newline.
++ flag.help += stringify(t2);
++ flag.help += ")";
++
++ add(flag);
++}
++
++
++template <typename Flags, typename T>
++void FlagsBase::add(
++ Option<T> Flags::*option,
++ const std::string& name,
++ const std::string& help)
++{
++ Flags* flags = dynamic_cast<Flags*>(this);
++ if (flags == NULL) {
++ std::cerr << "Attempted to add flag '" << name
++ << "' with incompatible type" << std::endl;
++ abort();
++ }
++
++ Flag flag;
++ flag.name = name;
++ flag.help = help;
++ flag.boolean = typeid(T) == typeid(bool);
++ flag.loader = std::tr1::bind(
++ &OptionMemberLoader<Flags, T>::load,
++ std::tr1::placeholders::_1,
++ option,
++ std::tr1::function<Try<T>(const std::string&)>(
++ std::tr1::bind(&parse<T>, std::tr1::placeholders::_1)),
++ name,
++ std::tr1::placeholders::_2);
++
++ add(flag);
++}
++
++
++inline void FlagsBase::add(const Flag& flag)
++{
++ if (flags.count(flag.name) > 0) {
++ EXIT(1) << "Attempted to add duplicate flag '" << flag.name << "'";
++ } else if (flag.name.find("no-") == 0) {
++ EXIT(1) << "Attempted to add flag '" << flag.name
++ << "' that starts with the reserved 'no-' prefix";
++ }
++
++ flags[flag.name] = flag;
++}
++
++
++// Extract environment variable "flags" with the specified prefix.
++inline std::map<std::string, Option<std::string> > FlagsBase::extract(
++ const std::string& prefix)
++{
++ char** environ = os::environ();
++
++ std::map<std::string, Option<std::string> > values;
++
++ for (int i = 0; environ[i] != NULL; i++) {
++ std::string variable = environ[i];
++ if (variable.find(prefix) == 0) {
++ size_t eq = variable.find_first_of("=");
++ if (eq == std::string::npos) {
++ continue; // Not expecting a missing '=', but ignore anyway.
++ }
++
++ std::string name = variable.substr(prefix.size(), eq - prefix.size());
++ name = strings::lower(name); // Allow PREFIX_NAME or PREFIX_name.
++
++ // Only add if it's a known flag.
++ if (flags.count(name) > 0 ||
++ (name.find("no-") == 0 && flags.count(name.substr(3)) > 0)) {
++ std::string value = variable.substr(eq + 1);
++ values[name] = Some(value);
++ }
++ }
++ }
++
++ return values;
++}
++
++
++inline Try<Nothing> FlagsBase::load(const std::string& prefix)
++{
++ return load(extract(prefix));
++}
++
++
++inline Try<Nothing> FlagsBase::load(
++ const Option<std::string>& prefix,
++ int argc,
++ char** argv,
++ bool unknowns,
++ bool duplicates)
++{
++ std::map<std::string, Option<std::string> > values;
++
++ if (prefix.isSome()) {
++ values = extract(prefix.get());
++ }
++
++ // Read flags from the command line.
++ for (int i = 1; i < argc; i++) {
++ const std::string arg(strings::trim(argv[i]));
++
++ if (arg.find("--") != 0) {
++ continue;
++ }
++
++ std::string name;
++ Option<std::string> value = None();
++
++ size_t eq = arg.find_first_of("=");
++ if (eq == std::string::npos && arg.find("--no-") == 0) { // --no-name
++ name = arg.substr(2);
++ } else if (eq == std::string::npos) { // --name
++ name = arg.substr(2);
++ } else { // --name=value
++ name = arg.substr(2, eq - 2);
++ value = arg.substr(eq + 1);
++ }
++
++ name = strings::lower(name);
++
++ if (!duplicates) {
++ if (values.count(name) > 0 ||
++ (name.find("no-") == 0 && values.count(name.substr(3)) > 0)) {
++ return Error("Duplicate flag '" + name + "' on command line");
++ }
++ }
++
++ values[name] = value;
++ }
++
++ return load(values, unknowns);
++}
++
++
++inline Try<Nothing> FlagsBase::load(
++ const std::string& prefix,
++ int argc,
++ char** argv,
++ bool unknowns,
++ bool duplicates)
++{
++ return load(Some(prefix), argc, argv, unknowns, duplicates);
++}
++
++
++inline Try<Nothing> FlagsBase::load(
++ const std::map<std::string, Option<std::string> >& values,
++ bool unknowns)
++{
++ std::map<std::string, Option<std::string> >::const_iterator iterator;
++
++ for (iterator = values.begin(); iterator != values.end(); ++iterator) {
++ const std::string& name = iterator->first;
++ const Option<std::string>& value = iterator->second;
++
++ if (flags.count(name) > 0) {
++ if (value.isSome()) { // --name=value
++ if (flags[name].boolean && value.get() == "") {
++ flags[name].loader(this, "true"); // Should never fail.
++ } else {
++ Try<Nothing> loader = flags[name].loader(this, value.get());
++ if (loader.isError()) {
++ return Error(
++ "Failed to load flag '" + name + "': " + loader.error());
++ }
++ }
++ } else { // --name
++ if (flags[name].boolean) {
++ flags[name].loader(this, "true"); // Should never fail.
++ } else {
++ return Error(
++ "Failed to load non-boolean flag '" + name + "': Missing value");
++ }
++ }
++ } else if (name.find("no-") == 0) {
++ if (flags.count(name.substr(3)) > 0) { // --no-name
++ if (flags[name.substr(3)].boolean) {
++ if (value.isNone() || value.get() == "") {
++ flags[name.substr(3)].loader(this, "false"); // Should never fail.
++ } else {
++ return Error(
++ "Failed to load boolean flag '" + name.substr(3) +
++ "' via '" + name + "' with value '" + value.get() + "'");
++ }
++ } else {
++ return Error(
++ "Failed to load non-boolean flag '" + name.substr(3) +
++ "' via '" + name + "'");
++ }
++ } else {
++ return Error(
++ "Failed to load unknown flag '" + name.substr(3) +
++ "' via '" + name + "'");
++ }
++ } else if (!unknowns) {
++ return Error("Failed to load unknown flag '" + name + "'");
++ }
++ }
++
++ return Nothing();
++}
++
++
++inline Try<Nothing> FlagsBase::load(
++ const std::map<std::string, std::string>& _values,
++ bool unknowns)
++{
++ std::map<std::string, Option<std::string> > values;
++ std::map<std::string, std::string>::const_iterator iterator;
++ for (iterator = _values.begin(); iterator != _values.end(); ++iterator) {
++ const std::string& name = iterator->first;
++ const std::string& value = iterator->second;
++ values[name] = Some(value);
++ }
++ return load(values, unknowns);
++}
++
++
++inline std::string FlagsBase::usage() const
++{
++ const int PAD = 5;
++
++ std::string usage;
++
++ std::map<std::string, std::string> col1; // key -> col 1 string
++
++ // Construct string for the first column and store width of column.
++ size_t width = 0;
++
++ foreachvalue (const flags::Flag& flag, *this) {
++ if (flag.boolean) {
++ col1[flag.name] = " --[no-]" + flag.name;
++ } else {
++ col1[flag.name] = " --" + flag.name + "=VALUE";
++ }
++ width = std::max(width, col1[flag.name].size());
++ }
++
++ foreachvalue (const flags::Flag& flag, *this) {
++ std::string line = col1[flag.name];
++
++ std::string pad(PAD + width - line.size(), ' ');
++ line += pad;
++
++ size_t pos1 = 0, pos2 = 0;
++ pos2 = flag.help.find_first_of("\n\r", pos1);
++ line += flag.help.substr(pos1, pos2 - pos1) + "\n";
++ usage += line;
++
++ while (pos2 != std::string::npos) { // Handle multi-line help strings.
++ line = "";
++ pos1 = pos2 + 1;
++ std::string pad2(PAD + width, ' ');
++ line += pad2;
++ pos2 = flag.help.find_first_of("\n\r", pos1);
++ line += flag.help.substr(pos1, pos2 - pos1) + "\n";
++ usage += line;
++ }
++ }
++ return usage;
++}
++
++} // namespace flags {
++
++#endif // __STOUT_FLAGS_FLAGS_HPP__
+diff --git a/include/mesos/stout/flags/loader.hpp b/include/mesos/stout/flags/loader.hpp
+new file mode 100644
+index 0000000..a6e0f58
+--- /dev/null
++++ b/include/mesos/stout/flags/loader.hpp
+@@ -0,0 +1,110 @@
++#ifndef __STOUT_FLAGS_LOADER_HPP__
++#define __STOUT_FLAGS_LOADER_HPP__
++
++#include <string>
++
++#include <tr1/functional>
++
++#include <stout/error.hpp>
++#include <stout/nothing.hpp>
++#include <stout/option.hpp>
++#include <stout/some.hpp>
++#include <stout/try.hpp>
++
++#include <stout/flags/parse.hpp>
++
++namespace flags {
++
++// Forward declaration.
++class FlagsBase;
++
++template <typename T>
++struct Loader
++{
++ static Try<Nothing> load(
++ T* flag,
++ const std::tr1::function<Try<T>(const std::string&)>& parse,
++ const std::string& name,
++ const std::string& value)
++ {
++ Try<T> t = parse(value);
++ if (t.isSome()) {
++ *flag = t.get();
++ } else {
++ return Error("Failed to load value '" + value + "': " + t.error());
++ }
++ return Nothing();
++ }
++};
++
++
++template <typename T>
++struct OptionLoader
++{
++ static Try<Nothing> load(
++ Option<T>* flag,
++ const std::tr1::function<Try<T>(const std::string&)>& parse,
++ const std::string& name,
++ const std::string& value)
++ {
++ Try<T> t = parse(value);
++ if (t.isSome()) {
++ *flag = Some(t.get());
++ } else {
++ return Error("Failed to load value '" + value + "': " + t.error());
++ }
++ return Nothing();
++ }
++};
++
++
++template <typename F, typename T>
++struct MemberLoader
++{
++ static Try<Nothing> load(
++ FlagsBase* base,
++ T F::*flag,
++ const std::tr1::function<Try<T>(const std::string&)>& parse,
++ const std::string& name,
++ const std::string& value)
++ {
++ F* f = dynamic_cast<F*>(base);
++ if (f != NULL) {
++ Try<T> t = parse(value);
++ if (t.isSome()) {
++ f->*flag = t.get();
++ } else {
++ return Error("Failed to load value '" + value + "': " + t.error());
++ }
++ }
++ return Nothing();
++ }
++};
++
++
++template <typename F, typename T>
++struct OptionMemberLoader
++{
++ static Try<Nothing> load(
++ FlagsBase* base,
++ Option<T> F::*flag,
++ const std::tr1::function<Try<T>(const std::string&)>& parse,
++ const std::string& name,
++ const std::string& value)
++ {
++ F* f = dynamic_cast<F*>(base);
++ if (f != NULL) {
++ Try<T> t = parse(value);
++ if (t.isSome()) {
++ f->*flag = Some(t.get());
++ } else {
++ return Error("Failed to load value '" + value + "': " + t.error());
++ }
++ }
++ return Nothing();
++ }
++};
++
++} // namespace flags {
++
++#endif // __STOUT_FLAGS_LOADER_HPP__
+diff --git a/include/mesos/stout/flags/parse.hpp b/include/mesos/stout/flags/parse.hpp
+new file mode 100644
+index 0000000..4c5b297
+--- /dev/null
++++ b/include/mesos/stout/flags/parse.hpp
+@@ -0,0 +1,62 @@
++#ifndef __STOUT_FLAGS_PARSE_HPP__
++#define __STOUT_FLAGS_PARSE_HPP__
++
++#include <sstream> // For istringstream.
++#include <string>
++
++#include <tr1/functional>
++
++#include <stout/duration.hpp>
++#include <stout/error.hpp>
++#include <stout/try.hpp>
++
++namespace flags {
++
++template <typename T>
++Try<T> parse(const std::string& value)
++{
++ T t;
++ std::istringstream in(value);
++ in >> t;
++ if (!in.good() && !in.eof()) {
++ return Error("Failed to convert into required type");
++ }
++ return t;
++}
++
++
++template <>
++inline Try<std::string> parse(const std::string& value)
++{
++ return value;
++}
++
++
++template <>
++inline Try<bool> parse(const std::string& value)
++{
++ if (value == "true" || value == "1") {
++ return true;
++ } else if (value == "false" || value == "0") {
++ return false;
++ }
++ return Error("Expecting a boolean (e.g., true or false)");
++}
++
++
++template <>
++inline Try<Duration> parse(const std::string& value)
++{
++ return Duration::parse(value);
++}
++
++
++template <>
++inline Try<Bytes> parse(const std::string& value)
++{
++ return Bytes::parse(value);
++}
++
++} // namespace flags {
++
++#endif // __STOUT_FLAGS_PARSE_HPP__
+diff --git a/include/mesos/stout/foreach.hpp b/include/mesos/stout/foreach.hpp
+new file mode 100644
+index 0000000..0afe285
+--- /dev/null
++++ b/include/mesos/stout/foreach.hpp
+@@ -0,0 +1,51 @@
++#ifndef __STOUT_FOREACH_HPP__
++#define __STOUT_FOREACH_HPP__
++
++#include <boost/foreach.hpp>
++
++#include <boost/tuple/tuple.hpp>
++
++namespace __foreach__ {
++
++// NOTE: This is a copied from Boost
++// (boost/tuple/detail/tuple_basic_no_partial_spec.hpp) because the
++// new 'boost::tuples::ignore' does not work in our 'foreachkey' and
++// 'foreachvalue'.
++struct swallow_assign {
++ template<typename T>
++ swallow_assign const& operator=(const T&) const {
++ return *this;
++ }
++};
++
++swallow_assign const ignore = swallow_assign();
++
++} // namespace __foreach__ {
++
++#define BOOST_FOREACH_PAIR(VARFIRST, VARSECOND, COL) \
++ BOOST_FOREACH_PREAMBLE() \
++ if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_col) = BOOST_FOREACH_CONTAIN(COL)) {} else \
++ if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_cur) = BOOST_FOREACH_BEGIN(COL)) {} else \
++ if (boost::foreach_detail_::auto_any_t BOOST_FOREACH_ID(_foreach_end) = BOOST_FOREACH_END(COL)) {} else \
++ for (bool BOOST_FOREACH_ID(_foreach_continue) = true, BOOST_FOREACH_ID(_foreach_onetime) = true; \
++ BOOST_FOREACH_ID(_foreach_continue) && !BOOST_FOREACH_DONE(COL); \
++ BOOST_FOREACH_ID(_foreach_continue) ? BOOST_FOREACH_NEXT(COL) : (void)0) \
++ if (boost::foreach_detail_::set_false(BOOST_FOREACH_ID(_foreach_onetime))) {} else \
++ for (VARFIRST = BOOST_FOREACH_DEREF(COL).first; \
++ !BOOST_FOREACH_ID(_foreach_onetime); \
++ BOOST_FOREACH_ID(_foreach_onetime) = true) \
++ if (boost::foreach_detail_::set_false(BOOST_FOREACH_ID(_foreach_continue))) {} else \
++ for (VARSECOND = BOOST_FOREACH_DEREF(COL).second; \
++ !BOOST_FOREACH_ID(_foreach_continue); \
++ BOOST_FOREACH_ID(_foreach_continue) = true)
++
++#define foreach BOOST_FOREACH
++#define foreachpair BOOST_FOREACH_PAIR
++
++#define foreachkey(VAR, COL) \
++ foreachpair (VAR, __foreach__::ignore, COL)
++
++#define foreachvalue(VAR, COL) \
++ foreachpair (__foreach__::ignore, VAR, COL)
++
++#endif // __STOUT_FOREACH_HPP__
+diff --git a/include/mesos/stout/format.hpp b/include/mesos/stout/format.hpp
+new file mode 100644
+index 0000000..cae7fcb
+--- /dev/null
++++ b/include/mesos/stout/format.hpp
+@@ -0,0 +1,382 @@
++#ifndef __STOUT_FORMAT_HPP__
++#define __STOUT_FORMAT_HPP__
++
++#include <stdarg.h> // For 'va_list', 'va_start', 'va_end'.
++#include <stdio.h> // For 'vasprintf'.
++
++#include <string>
++
++#include <tr1/type_traits> // For 'is_pod'.
++
++#include "error.hpp"
++#include "try.hpp"
++#include "stringify.hpp"
++
++
++// The 'strings::format' functions produces strings based on the
++// printf family of functions. Except, unlike the printf family of
++// functions, the 'strings::format' routines attempt to "stringify"
++// any arguments that are not POD types (i.e., "plain old data":
++// primitives, pointers, certain structs/classes and unions,
++// etc). This enables passing structs/classes to 'strings::format'
++// provided there is a definition/specialization of 'ostream::operator
++// <<' available for that type. Note that the '%s' format specifier is
++// expected for each argument that gets stringified. A specialization
++// for std::string is also provided so that std::string::c_str is not
++// necessary (but again, '%s' is expected as the format specifier).
++
++namespace strings {
++namespace internal {
++
++Try<std::string> format(const std::string& fmt, va_list args);
++Try<std::string> format(const std::string& fmt, ...);
++
++template <typename T, bool b>
++struct stringify;
++
++} // namespace internal {
++
++
++#if __cplusplus >= 201103L
++template <typename ...T>
++Try<std::string> format(const std::string& s, const T& ...t)
++{
++ return internal::format(
++ s,
++ internal::stringify<T, !std::is_pod<T>::value>(t).get()...);
++}
++#else
++template <typename T1>
++Try<std::string> format(const std::string& s,
++ const T1& t1)
++{
++ return internal::format(
++ s,
++ internal::stringify<T1, !std::tr1::is_pod<T1>::value>(t1).get());
++}
++
++
++template <typename T1,
++ typename T2>
++Try<std::string> format(const std::string& s,
++ const T1& t1,
++ const T2& t2)
++{
++ return internal::format(
++ s,
++ internal::stringify<T1, !std::tr1::is_pod<T1>::value>(t1).get(),
++ internal::stringify<T2, !std::tr1::is_pod<T1>::value>(t2).get());
++}
++
++
++template <typename T1,
++ typename T2,
++ typename T3>
++Try<std::string> format(const std::string& s,
++ const T1& t1,
++ const T2& t2,
++ const T3& t3)
++{
++ return internal::format(
++ s,
++ internal::stringify<T1, !std::tr1::is_pod<T1>::value>(t1).get(),
++ internal::stringify<T2, !std::tr1::is_pod<T2>::value>(t2).get(),
++ internal::stringify<T3, !std::tr1::is_pod<T3>::value>(t3).get());
++}
++
++
++template <typename T1,
++ typename T2,
++ typename T3,
++ typename T4>
++Try<std::string> format(const std::string& s,
++ const T1& t1,
++ const T2& t2,
++ const T3& t3,
++ const T4& t4)
++{
++ return internal::format(
++ s,
++ internal::stringify<T1, !std::tr1::is_pod<T1>::value>(t1).get(),
++ internal::stringify<T2, !std::tr1::is_pod<T2>::value>(t2).get(),
++ internal::stringify<T3, !std::tr1::is_pod<T3>::value>(t3).get(),
++ internal::stringify<T4, !std::tr1::is_pod<T4>::value>(t4).get());
++}
++
++
++template <typename T1,
++ typename T2,
++ typename T3,
++ typename T4,
++ typename T5>
++Try<std::string> format(const std::string& s,
++ const T1& t1,
++ const T2& t2,
++ const T3& t3,
++ const T4& t4,
++ const T5& t5)
++{
++ return internal::format(
++ s,
++ internal::stringify<T1, !std::tr1::is_pod<T1>::value>(t1).get(),
++ internal::stringify<T2, !std::tr1::is_pod<T2>::value>(t2).get(),
++ internal::stringify<T3, !std::tr1::is_pod<T3>::value>(t3).get(),
++ internal::stringify<T4, !std::tr1::is_pod<T4>::value>(t4).get(),
++ internal::stringify<T5, !std::tr1::is_pod<T5>::value>(t5).get());
++}
++
++
++template <typename T1,
++ typename T2,
++ typename T3,
++ typename T4,
++ typename T5,
++ typename T6>
++Try<std::string> format(const std::string& s,
++ const T1& t1,
++ const T2& t2,
++ const T3& t3,
++ const T4& t4,
++ const T5& t5,
++ const T6& t6)
++{
++ return internal::format(
++ s,
++ internal::stringify<T1, !std::tr1::is_pod<T1>::value>(t1).get(),
++ internal::stringify<T2, !std::tr1::is_pod<T2>::value>(t2).get(),
++ internal::stringify<T3, !std::tr1::is_pod<T3>::value>(t3).get(),
++ internal::stringify<T4, !std::tr1::is_pod<T4>::value>(t4).get(),
++ internal::stringify<T5, !std::tr1::is_pod<T5>::value>(t5).get(),
++ internal::stringify<T6, !std::tr1::is_pod<T6>::value>(t6).get());
++}
++
++
++template <typename T1,
++ typename T2,
++ typename T3,
++ typename T4,
++ typename T5,
++ typename T6,
++ typename T7>
++Try<std::string> format(const std::string& s,
++ const T1& t1,
++ const T2& t2,
++ const T3& t3,
++ const T4& t4,
++ const T5& t5,
++ const T6& t6,
++ const T7& t7)
++{
++ return internal::format(
++ s,
++ internal::stringify<T1, !std::tr1::is_pod<T1>::value>(t1).get(),
++ internal::stringify<T2, !std::tr1::is_pod<T2>::value>(t2).get(),
++ internal::stringify<T3, !std::tr1::is_pod<T3>::value>(t3).get(),
++ internal::stringify<T4, !std::tr1::is_pod<T4>::value>(t4).get(),
++ internal::stringify<T5, !std::tr1::is_pod<T5>::value>(t5).get(),
++ internal::stringify<T6, !std::tr1::is_pod<T6>::value>(t6).get(),
++ internal::stringify<T7, !std::tr1::is_pod<T7>::value>(t7).get());
++}
++
++
++template <typename T1,
++ typename T2,
++ typename T3,
++ typename T4,
++ typename T5,
++ typename T6,
++ typename T7,
++ typename T8>
++Try<std::string> format(const std::string& s,
++ const T1& t1,
++ const T2& t2,
++ const T3& t3,
++ const T4& t4,
++ const T5& t5,
++ const T6& t6,
++ const T7& t7,
++ const T8& t8)
++{
++ return internal::format(
++ s,
++ internal::stringify<T1, !std::tr1::is_pod<T1>::value>(t1).get(),
++ internal::stringify<T2, !std::tr1::is_pod<T2>::value>(t2).get(),
++ internal::stringify<T3, !std::tr1::is_pod<T3>::value>(t3).get(),
++ internal::stringify<T4, !std::tr1::is_pod<T4>::value>(t4).get(),
++ internal::stringify<T5, !std::tr1::is_pod<T5>::value>(t5).get(),
++ internal::stringify<T6, !std::tr1::is_pod<T6>::value>(t6).get(),
++ internal::stringify<T7, !std::tr1::is_pod<T7>::value>(t7).get(),
++ internal::stringify<T8, !std::tr1::is_pod<T8>::value>(t8).get());
++}
++
++
++template <typename T1,
++ typename T2,
++ typename T3,
++ typename T4,
++ typename T5,
++ typename T6,
++ typename T7,
++ typename T8,
++ typename T9>
++Try<std::string> format(const std::string& s,
++ const T1& t1,
++ const T2& t2,
++ const T3& t3,
++ const T4& t4,
++ const T5& t5,
++ const T6& t6,
++ const T7& t7,
++ const T8& t8,
++ const T9& t9)
++{
++ return internal::format(
++ s,
++ internal::stringify<T1, !std::tr1::is_pod<T1>::value>(t1).get(),
++ internal::stringify<T2, !std::tr1::is_pod<T2>::value>(t2).get(),
++ internal::stringify<T3, !std::tr1::is_pod<T3>::value>(t3).get(),
++ internal::stringify<T4, !std::tr1::is_pod<T4>::value>(t4).get(),
++ internal::stringify<T5, !std::tr1::is_pod<T5>::value>(t5).get(),
++ internal::stringify<T6, !std::tr1::is_pod<T6>::value>(t6).get(),
++ internal::stringify<T7, !std::tr1::is_pod<T7>::value>(t7).get(),
++ internal::stringify<T8, !std::tr1::is_pod<T8>::value>(t8).get(),
++ internal::stringify<T9, !std::tr1::is_pod<T9>::value>(t9).get());
++}
++
++
++template <typename T1,
++ typename T2,
++ typename T3,
++ typename T4,
++ typename T5,
++ typename T6,
++ typename T7,
++ typename T8,
++ typename T9,
++ typename T10>
++Try<std::string> format(const std::string& s,
++ const T1& t1,
++ const T2& t2,
++ const T3& t3,
++ const T4& t4,
++ const T5& t5,
++ const T6& t6,
++ const T7& t7,
++ const T8& t8,
++ const T9& t9,
++ const T10& t10)
++{
++ return internal::format(
++ s,
++ internal::stringify<T1, !std::tr1::is_pod<T1>::value>(t1).get(),
++ internal::stringify<T2, !std::tr1::is_pod<T2>::value>(t2).get(),
++ internal::stringify<T3, !std::tr1::is_pod<T3>::value>(t3).get(),
++ internal::stringify<T4, !std::tr1::is_pod<T4>::value>(t4).get(),
++ internal::stringify<T5, !std::tr1::is_pod<T5>::value>(t5).get(),
++ internal::stringify<T6, !std::tr1::is_pod<T6>::value>(t6).get(),
++ internal::stringify<T7, !std::tr1::is_pod<T7>::value>(t7).get(),
++ internal::stringify<T8, !std::tr1::is_pod<T8>::value>(t8).get(),
++ internal::stringify<T9, !std::tr1::is_pod<T9>::value>(t9).get(),
++ internal::stringify<T10, !std::tr1::is_pod<T10>::value>(t10).get());
++}
++
++template <typename T1,
++ typename T2,
++ typename T3,
++ typename T4,
++ typename T5,
++ typename T6,
++ typename T7,
++ typename T8,
++ typename T9,
++ typename T10,
++ typename T11>
++Try<std::string> format(const std::string& s,
++ const T1& t1,
++ const T2& t2,
++ const T3& t3,
++ const T4& t4,
++ const T5& t5,
++ const T6& t6,
++ const T7& t7,
++ const T8& t8,
++ const T9& t9,
++ const T10& t10,
++ const T11& t11)
++{
++ return internal::format(
++ s,
++ internal::stringify<T1, !std::tr1::is_pod<T1>::value>(t1).get(),
++ internal::stringify<T2, !std::tr1::is_pod<T2>::value>(t2).get(),
++ internal::stringify<T3, !std::tr1::is_pod<T3>::value>(t3).get(),
++ internal::stringify<T4, !std::tr1::is_pod<T4>::value>(t4).get(),
++ internal::stringify<T5, !std::tr1::is_pod<T5>::value>(t5).get(),
++ internal::stringify<T6, !std::tr1::is_pod<T6>::value>(t6).get(),
++ internal::stringify<T7, !std::tr1::is_pod<T7>::value>(t7).get(),
++ internal::stringify<T8, !std::tr1::is_pod<T8>::value>(t8).get(),
++ internal::stringify<T9, !std::tr1::is_pod<T9>::value>(t9).get(),
++ internal::stringify<T10, !std::tr1::is_pod<T10>::value>(t10).get(),
++ internal::stringify<T11, !std::tr1::is_pod<T11>::value>(t11).get());
++}
++#endif // __cplusplus >= 201103L
++
++
++namespace internal {
++
++inline Try<std::string> format(const std::string& fmt, va_list args)
++{
++ char* temp;
++ if (vasprintf(&temp, fmt.c_str(), args) == -1) {
++ // Note that temp is undefined, so we do not need to call free.
++ return Error("Failed to format '" + fmt + "' (possibly out of memory)");
++ }
++ std::string result(temp);
++ free(temp);
++ return result;
++}
++
++
++inline Try<std::string> format(const std::string& fmt, ...)
++{
++ va_list args;
++ va_start(args, fmt);
++ const Try<std::string>& result = format(fmt, args);
++ va_end(args);
++ return result;
++}
++
++
++template <typename T>
++struct stringify<T, false>
++{
++ stringify(const T& _t) : t(_t) {}
++ const T& get() { return t; }
++ const T& t;
++};
++
++
++template <typename T>
++struct stringify<T, true>
++{
++ stringify(const T& _t) : s(::stringify(_t)) {}
++ const char* get() { return s.c_str(); }
++
++ // NOTE: We need to do the copy here, because the temporary returned by
++ // ::stringify() doesn't outlive the get() call inside strings::format().
++ // TODO(vinod): Figure out a fix for using const ref here.
++ const std::string s;
++};
++
++
++template <>
++struct stringify<std::string, true>
++{
++ stringify(const std::string& _s) : s(_s) {}
++ const char* get() { return s.c_str(); }
++ const std::string& s;
++};
++
++} // namespace internal {
++} // namespace strings {
++
++#endif // __STOUT_FORMAT_HPP__
+diff --git a/include/mesos/stout/fs.hpp b/include/mesos/stout/fs.hpp
+new file mode 100644
+index 0000000..3a20e86
+--- /dev/null
++++ b/include/mesos/stout/fs.hpp
+@@ -0,0 +1,54 @@
++#ifndef __STOUT_FS_HPP__
++#define __STOUT_FS_HPP__
++
++#include <unistd.h> // For symlink.
++
++#include <sys/statvfs.h>
++
++#include <string>
++
++#include "bytes.hpp"
++#include "error.hpp"
++#include "nothing.hpp"
++#include "try.hpp"
++
++// TODO(bmahler): Merge available() and usage() into df() that returns
++// a struct, and move this back into os.hpp.
++namespace fs {
++
++// Returns the total disk size in bytes.
++inline Try<Bytes> size(const std::string& path = "/")
++{
++ struct statvfs buf;
++ if (::statvfs(path.c_str(), &buf) < 0) {
++ return ErrnoError();
++ }
++ return Bytes(buf.f_blocks * buf.f_frsize);
++}
++
++
++// Returns relative disk usage of the file system that the given path
++// is mounted at.
++inline Try<double> usage(const std::string& path = "/")
++{
++ struct statvfs buf;
++ if (statvfs(path.c_str(), &buf) < 0) {
++ return ErrnoError("Error invoking statvfs on '" + path + "'");
++ }
++ return (double) (buf.f_blocks - buf.f_bfree) / buf.f_blocks;
++}
++
++
++inline Try<Nothing> symlink(
++ const std::string& original,
++ const std::string& link)
++{
++ if (::symlink(original.c_str(), link.c_str()) < 0) {
++ return ErrnoError();
++ }
++ return Nothing();
++}
++
++} // namespace fs {
++
++#endif // __STOUT_FS_HPP__
+diff --git a/include/mesos/stout/gtest.hpp b/include/mesos/stout/gtest.hpp
+new file mode 100644
+index 0000000..1f10834
+--- /dev/null
++++ b/include/mesos/stout/gtest.hpp
+@@ -0,0 +1,130 @@
++#ifndef __STOUT_GTEST_HPP__
++#define __STOUT_GTEST_HPP__
++
++#include <gtest/gtest.h>
++
++#include <string>
++
++#include <stout/option.hpp>
++#include <stout/result.hpp>
++#include <stout/try.hpp>
++
++
++template <typename T>
++::testing::AssertionResult AssertSome(
++ const char* expr,
++ const Option<T>& actual)
++{
++ if (actual.isNone()) {
++ return ::testing::AssertionFailure()
++ << expr << " is NONE";
++ }
++
++ return ::testing::AssertionSuccess();
++}
++
++
++template <typename T>
++::testing::AssertionResult AssertSome(
++ const char* expr,
++ const Try<T>& actual)
++{
++ if (actual.isError()) {
++ return ::testing::AssertionFailure()
++ << expr << ": " << actual.error();
++ }
++
++ return ::testing::AssertionSuccess();
++}
++
++
++template <typename T>
++::testing::AssertionResult AssertSome(
++ const char* expr,
++ const Result<T>& actual)
++{
++ if (actual.isNone()) {
++ return ::testing::AssertionFailure()
++ << expr << " is NONE";
++ } else if (actual.isError()) {
++ return ::testing::AssertionFailure()
++ << expr << ": " << actual.error();
++ }
++
++ return ::testing::AssertionSuccess();
++}
++
++
++template <typename T1, typename T2>
++::testing::AssertionResult AssertSomeEq(
++ const char* expectedExpr,
++ const char* actualExpr,
++ const T1& expected,
++ const T2& actual) // Duck typing!
++{
++ const ::testing::AssertionResult result = AssertSome(actualExpr, actual);
++
++ if (result) {
++ if (expected == actual.get()) {
++ return ::testing::AssertionSuccess();
++ } else {
++ return ::testing::AssertionFailure()
++ << "Value of: (" << actualExpr << ").get()\n"
++ << " Actual: " << ::testing::PrintToString(actual.get()) << "\n"
++ << "Expected: " << expectedExpr << "\n"
++ << "Which is: " << ::testing::PrintToString(expected);
++ }
++ }
++
++ return result;
++}
++
++
++#define ASSERT_SOME(actual) \
++ ASSERT_PRED_FORMAT1(AssertSome, actual)
++
++
++#define EXPECT_SOME(actual) \
++ EXPECT_PRED_FORMAT1(AssertSome, actual)
++
++
++#define ASSERT_SOME_EQ(expected, actual) \
++ ASSERT_PRED_FORMAT2(AssertSomeEq, expected, actual)
++
++
++#define EXPECT_SOME_EQ(expected, actual) \
++ EXPECT_PRED_FORMAT2(AssertSomeEq, expected, actual)
++
++
++#define ASSERT_SOME_TRUE(actual) \
++ ASSERT_PRED_FORMAT2(AssertSomeEq, true, actual)
++
++
++#define EXPECT_SOME_TRUE(actual) \
++ EXPECT_PRED_FORMAT2(AssertSomeEq, true, actual)
++
++
++#define ASSERT_SOME_FALSE(actual) \
++ ASSERT_PRED_FORMAT2(AssertSomeEq, false, actual)
++
++
++#define EXPECT_SOME_FALSE(actual) \
++ EXPECT_PRED_FORMAT2(AssertSomeEq, false, actual)
++
++
++#define ASSERT_ERROR(actual) \
++ ASSERT_TRUE(actual.isError())
++
++
++#define EXPECT_ERROR(actual) \
++ EXPECT_TRUE(actual.isError())
++
++
++#define ASSERT_NONE(actual) \
++ ASSERT_TRUE(actual.isNone())
++
++
++#define EXPECT_NONE(actual) \
++ EXPECT_TRUE(actual.isNone())
++
++#endif // __STOUT_GTEST_HPP__
+diff --git a/include/mesos/stout/gzip.hpp b/include/mesos/stout/gzip.hpp
+new file mode 100644
+index 0000000..bc0d818
+--- /dev/null
++++ b/include/mesos/stout/gzip.hpp
+@@ -0,0 +1,135 @@
++#ifndef __STOUT_GZIP_HPP__
++#define __STOUT_GZIP_HPP__
++
++#include <zlib.h>
++
++#include <string>
++
++#include "error.hpp"
++#include "try.hpp"
++
++// Compression utilities.
++// TODO(bmahler): Provide streaming compression / decompression as well.
++namespace gzip {
++
++// We use a 16KB buffer with zlib compression / decompression.
++#define GZIP_BUFFER_SIZE 16384
++
++// Returns a gzip compressed version of the provided string.
++// The compression level should be within the range [-1, 9].
++// See zlib.h:
++// #define Z_NO_COMPRESSION 0
++// #define Z_BEST_SPEED 1
++// #define Z_BEST_COMPRESSION 9
++// #define Z_DEFAULT_COMPRESSION (-1)
++inline Try<std::string> compress(
++ const std::string& decompressed,
++ int level = Z_DEFAULT_COMPRESSION)
++{
++ // Verify the level is within range.
++ if (!(level == Z_DEFAULT_COMPRESSION ||
++ (level >= Z_NO_COMPRESSION && level <= Z_BEST_COMPRESSION))) {
++ return Error("Invalid compression level: " + level);
++ }
++
++ z_stream_s stream;
++ stream.next_in =
++ const_cast<Bytef*>(reinterpret_cast<const Bytef*>(decompressed.data()));
++ stream.avail_in = decompressed.length();
++ stream.zalloc = Z_NULL;
++ stream.zfree = Z_NULL;
++ stream.opaque = Z_NULL;
++
++ int code = deflateInit2(
++ &stream,
++ level, // Compression level.
++ Z_DEFLATED, // Compression method.
++ MAX_WBITS + 16, // Zlib magic for gzip compression / decompression.
++ 8, // Default memLevel value.
++ Z_DEFAULT_STRATEGY);
++
++ if (code != Z_OK) {
++ return Error("Failed to initialize zlib: " + std::string(stream.msg));
++ }
++
++ // Build up the compressed result.
++ Bytef buffer[GZIP_BUFFER_SIZE];
++ std::string result = "";
++ do {
++ stream.next_out = buffer;
++ stream.avail_out = GZIP_BUFFER_SIZE;
++ code = deflate(&stream, stream.avail_in > 0 ? Z_NO_FLUSH : Z_FINISH);
++
++ if (code != Z_OK && code != Z_STREAM_END) {
++ Error error(std::string(stream.msg));
++ deflateEnd(&stream);
++ return error;
++ }
++
++ // Consume output and reset the buffer.
++ result.append(
++ reinterpret_cast<char*>(buffer),
++ GZIP_BUFFER_SIZE - stream.avail_out);
++ stream.next_out = buffer;
++ stream.avail_out = GZIP_BUFFER_SIZE;
++ } while (code != Z_STREAM_END);
++
++ code = deflateEnd(&stream);
++ if (code != Z_OK) {
++ return Error("Failed to clean up zlib: " + std::string(stream.msg));
++ }
++ return result;
++}
++
++
++// Returns a gzip decompressed version of the provided string.
++inline Try<std::string> decompress(const std::string& compressed)
++{
++ z_stream_s stream;
++ stream.next_in =
++ const_cast<Bytef*>(reinterpret_cast<const Bytef*>(compressed.data()));
++ stream.avail_in = compressed.length();
++ stream.zalloc = Z_NULL;
++ stream.zfree = Z_NULL;
++ stream.opaque = Z_NULL;
++
++ int code = inflateInit2(
++ &stream,
++ MAX_WBITS + 16); // Zlib magic for gzip compression / decompression.
++
++ if (code != Z_OK) {
++ return Error("Failed to initialize zlib: " + std::string(stream.msg));
++ }
++
++ // Build up the decompressed result.
++ Bytef buffer[GZIP_BUFFER_SIZE];
++ std::string result = "";
++ do {
++ stream.next_out = buffer;
++ stream.avail_out = GZIP_BUFFER_SIZE;
++ code = inflate(&stream, stream.avail_in > 0 ? Z_NO_FLUSH : Z_FINISH);
++
++ if (code != Z_OK && code != Z_STREAM_END) {
++ Error error(std::string(stream.msg));
++ inflateEnd(&stream);
++ return error;
++ }
++
++ // Consume output and reset the buffer.
++ result.append(
++ reinterpret_cast<char*>(buffer),
++ GZIP_BUFFER_SIZE - stream.avail_out);
++ stream.next_out = buffer;
++ stream.avail_out = GZIP_BUFFER_SIZE;
++ } while (code != Z_STREAM_END);
++
++ code = inflateEnd(&stream);
++ if (code != Z_OK) {
++ return Error("Failed to clean up zlib: " + std::string(stream.msg));
++ }
++ return result;
++}
++
++} // namespace gzip {
++
++#endif // __STOUT_GZIP_HPP__
+diff --git a/include/mesos/stout/hashmap.hpp b/include/mesos/stout/hashmap.hpp
+new file mode 100644
+index 0000000..dc78e28
+--- /dev/null
++++ b/include/mesos/stout/hashmap.hpp
+@@ -0,0 +1,113 @@
++#ifndef __STOUT_HASHMAP_HPP__
++#define __STOUT_HASHMAP_HPP__
++
++#include <utility>
++
++#include <boost/get_pointer.hpp>
++#include <boost/unordered_map.hpp>
++
++#include "hashset.hpp"
++#include "foreach.hpp"
++#include "none.hpp"
++#include "option.hpp"
++
++
++// Provides a hash map via Boost's 'unordered_map'. For most intensive
++// purposes this could be accomplished with a templated typedef, but
++// those don't exist (until C++-11). Also, doing it this way allows us
++// to add functionality, or better naming of existing functionality,
++// etc.
++
++template <typename Key, typename Value>
++class hashmap : public boost::unordered_map<Key, Value>
++{
++public:
++ // An explicit default constructor is needed so
++ // 'const hashmap<T> map;' is not an error.
++ hashmap() {}
++
++ // Checks whether this map contains a binding for a key.
++ bool contains(const Key& key) const
++ {
++ return boost::unordered_map<Key, Value>::count(key) > 0;
++ }
++
++ // Checks whether there exists a bound value in this map.
++ bool containsValue(const Value& v) const
++ {
++ foreachvalue (const Value& value, *this) {
++ if (value == v) {
++ return true;
++ }
++ }
++ }
++
++ // Inserts a key, value pair into the map replacing an old value
++ // if the key is already present.
++ void put(const Key& key, const Value& value)
++ {
++ boost::unordered_map<Key, Value>::erase(key);
++ boost::unordered_map<Key, Value>::insert(std::pair<Key, Value>(key, value));
++ }
++
++ // Returns an Option for the binding to the key.
++ Option<Value> get(const Key& key) const
++ {
++ typedef typename boost::unordered_map<Key, Value>::const_iterator
++ const_iterator;
++ const_iterator it = boost::unordered_map<Key, Value>::find(key);
++ if (it == boost::unordered_map<Key, Value>::end()) {
++ return None();
++ }
++ return it->second;
++ }
++
++ // Returns the set of keys in this map.
++ // TODO(vinod/bmahler): Should return a list instead.
++ hashset<Key> keys() const
++ {
++ hashset<Key> result;
++ foreachkey (const Key& key, *this) {
++ result.insert(key);
++ }
++ return result;
++ }
++
++ // Returns the list of values in this map.
++ std::list<Value> values() const
++ {
++ std::list<Value> result;
++ foreachvalue (const Value& value, *this) {
++ result.push_back(value);
++ }
++ return result;
++ }
++
++ // Checks whether there exists a value in this map that returns the
++ // a result equal to 'r' when the specified method is invoked.
++ template <typename R, typename T>
++ bool existsValue(R (T::*method)(), R r) const
++ {
++ foreachvalue (const Value& value, *this) {
++ const T* t = boost::get_pointer(value);
++ if (t->*method() == r) {
++ return true;
++ }
++ }
++ }
++
++ // Checks whether there exists a value in this map whose specified
++ // member is equal to 'r'.
++ template <typename R, typename T>
++ bool existsValue(R (T::*member), R r) const
++ {
++ foreachvalue (const Value& value, *this) {
++ const T* t = boost::get_pointer(value);
++ if (t->*member == r) {
++ return true;
++ }
++ }
++ }
++};
++
++#endif // __STOUT_HASHMAP_HPP__
+diff --git a/include/mesos/stout/hashset.hpp b/include/mesos/stout/hashset.hpp
+new file mode 100644
+index 0000000..f1f2099
+--- /dev/null
++++ b/include/mesos/stout/hashset.hpp
+@@ -0,0 +1,69 @@
++#ifndef __STOUT_HASHSET_HPP__
++#define __STOUT_HASHSET_HPP__
++
++#include <boost/get_pointer.hpp>
++#include <boost/unordered_set.hpp>
++
++#include "foreach.hpp"
++
++
++// Provides a hash set via Boost's 'unordered_set'. For most intensive
++// purposes this could be accomplished with a templated typedef, but
++// those don't exist (until C++-11). Also, doing it this way allows us
++// to add functionality, or better naming of existing functionality,
++// etc.
++
++template <typename Elem>
++class hashset : public boost::unordered_set<Elem>
++{
++public:
++ // An explicit default constructor is needed so
++ // 'const hashset<T> map;' is not an error.
++ hashset() {}
++
++ // Checks whether this map contains a binding for a key.
++ bool contains(const Elem& elem) const
++ {
++ return boost::unordered_set<Elem>::count(elem) > 0;
++ }
++
++ // Checks whether there exists a value in this set that returns the
++ // a result equal to 'r' when the specified method is invoked.
++ template <typename R, typename T>
++ bool exists(R (T::*method)(), R r) const
++ {
++ foreach (const Elem& elem, *this) {
++ const T* t = boost::get_pointer(elem);
++ if (t->*method() == r) {
++ return true;
++ }
++ }
++ }
++
++ // Checks whether there exists an element in this set whose
++ // specified member is equal to 'r'.
++ template <typename R, typename T>
++ bool exists(R (T::*member), R r) const
++ {
++ foreach (const Elem& elem, *this) {
++ const T* t = boost::get_pointer(elem);
++ if (t->*member == r) {
++ return true;
++ }
++ }
++ }
++};
++
++
++// Union operator.
++template <typename Elem>
++hashset<Elem> operator | (const hashset<Elem>& left, const hashset<Elem>& right)
++{
++ // Note, we're not using 'set_union' since it affords us no benefit
++ // in efficiency and is more complicated to use given we have sets.
++ hashset<Elem> result = left;
++ result.insert(right.begin(), right.end());
++ return result;
++}
++
++#endif // __STOUT_HASHSET_HPP__
+diff --git a/include/mesos/stout/json.hpp b/include/mesos/stout/json.hpp
+new file mode 100644
+index 0000000..4406d07
+--- /dev/null
++++ b/include/mesos/stout/json.hpp
+@@ -0,0 +1,205 @@
++#ifndef __STOUT_JSON__
++#define __STOUT_JSON__
++
++#include <iomanip>
++#include <iostream>
++#include <limits>
++#include <list>
++#include <map>
++#include <string>
++
++#include <boost/variant.hpp>
++
++#include <stout/foreach.hpp>
++
++// TODO(jsirois): Implement parsing that constructs JSON objects.
++
++// TODO(bmahler): Evaluate picojson / JSON_Spirit.
++namespace JSON {
++
++// Implementation of the JavaScript Object Notation (JSON) grammar
++// using boost::variant. We explicitly define each "type" of the
++// grammar, including 'true' (json::True), 'false' (json::False), and
++// 'null' (json::Null), for clarity and also because boost::variant
++// "picks" the wrong type when we try and use std::string, long (or
++// int), double (or float), and bool all in the same variant (while it
++// does work with explicit casts, it seemed bad style to force people
++// to put those casts in place). We could have avoided using
++// json::String or json::Number and just used std::string and double
++// respectively, but we choose to include them for completeness
++// (although, this does pay a 2x cost when compiling thanks to all the
++// extra template instantiations).
++
++struct String;
++struct Number;
++struct Object;
++struct Array;
++struct True;
++struct False;
++struct Null;
++
++
++typedef boost::variant<boost::recursive_wrapper<String>,
++ boost::recursive_wrapper<Number>,
++ boost::recursive_wrapper<Object>,
++ boost::recursive_wrapper<Array>,
++ boost::recursive_wrapper<True>,
++ boost::recursive_wrapper<False>,
++ boost::recursive_wrapper<Null> > Value;
++
++
++struct String
++{
++ String() {}
++ String(const char* _value) : value(_value) {}
++ String(const std::string& _value) : value(_value) {}
++ std::string value;
++};
++
++
++struct Number
++{
++ Number() : value(0) {}
++ Number(double _value) : value(_value) {}
++ double value;
++};
++
++
++struct Object
++{
++ std::map<std::string, Value> values;
++};
++
++
++struct Array
++{
++ std::list<Value> values;
++};
++
++
++struct True {};
++
++
++struct False {};
++
++
++struct Null {};
++
++
++// Implementation of rendering JSON objects built above using standard
++// C++ output streams. The visitor pattern is used thanks to to build
++// a "renderer" with boost::static_visitor and two top-level render
++// routines are provided for rendering JSON objects and arrays.
++
++struct Renderer : boost::static_visitor<>
++{
++ Renderer(std::ostream& _out) : out(_out) {}
++
++ void operator () (const String& string) const
++ {
++ // TODO(benh): This escaping DOES NOT handle unicode, it encodes as ASCII.
++ // See RFC4627 for the JSON string specificiation.
++ out << "\"";
++ foreach (unsigned char c, string.value) {
++ switch (c) {
++ case '"': out << "\\\""; break;
++ case '\\': out << "\\\\"; break;
++ case '/': out << "\\/"; break;
++ case '\b': out << "\\b"; break;
++ case '\f': out << "\\f"; break;
++ case '\n': out << "\\n"; break;
++ case '\r': out << "\\r"; break;
++ case '\t': out << "\\t"; break;
++ default:
++ // See RFC4627 for these ranges.
++ if ((c >= 0x20 && c <= 0x21) ||
++ (c >= 0x23 && c <= 0x5B) ||
++ (c >= 0x5D && c < 0x7F)) {
++ out << c;
++ } else {
++ // NOTE: We also escape all bytes > 0x7F since they imply more than
++ // 1 byte in UTF-8. This is why we don't escape UTF-8 properly.
++ // See RFC4627 for the escaping format: \uXXXX (X is a hex digit).
++ // Each byte here will be of the form: \u00XX (this is why we need
++ // setw and the cast to unsigned int).
++ out << "\\u" << std::setfill('0') << std::setw(4)
++ << std::hex << std::uppercase << (unsigned int) c;
++ }
++ break;
++ }
++ }
++ out << "\"";
++ }
++
++ void operator () (const Number& number) const
++ {
++ // Use the guaranteed accurate precision, see:
++ // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2005.pdf
++ out << std::setprecision(std::numeric_limits<double>::digits10)
++ << number.value;
++ }
++
++ void operator () (const Object& object) const
++ {
++ out << "{";
++ std::map<std::string, Value>::const_iterator iterator;
++ iterator = object.values.begin();
++ while (iterator != object.values.end()) {
++ out << "\"" << (*iterator).first << "\":";
++ boost::apply_visitor(Renderer(out), (*iterator).second);
++ if (++iterator != object.values.end()) {
++ out << ",";
++ }
++ }
++ out << "}";
++ }
++
++ void operator () (const Array& array) const
++ {
++ out << "[";
++ std::list<Value>::const_iterator iterator;
++ iterator = array.values.begin();
++ while (iterator != array.values.end()) {
++ boost::apply_visitor(Renderer(out), *iterator);
++ if (++iterator != array.values.end()) {
++ out << ",";
++ }
++ }
++ out << "]";
++ }
++
++ void operator () (const True&) const
++ {
++ out << "true";
++ }
++
++ void operator () (const False&) const
++ {
++ out << "false";
++ }
++
++ void operator () (const Null&) const
++ {
++ out << "null";
++ }
++
++private:
++ std::ostream& out;
++};
++
++
++inline void render(std::ostream& out, const Value& value)
++{
++ boost::apply_visitor(Renderer(out), value);
++}
++
++
++inline std::ostream& operator<<(std::ostream& out, const JSON::Value& value)
++{
++ JSON::render(out, value);
++ return out;
++}
++
++} // namespace JSON {
++
++#endif // __STOUT_JSON__
+diff --git a/include/mesos/stout/lambda.hpp b/include/mesos/stout/lambda.hpp
+new file mode 100644
+index 0000000..d493353
+--- /dev/null
++++ b/include/mesos/stout/lambda.hpp
+@@ -0,0 +1,14 @@
++#ifndef __STOUT_LAMBDA_HPP__
++#define __STOUT_LAMBDA_HPP__
++
++#include <tr1/functional>
++
++namespace lambda {
++
++using std::tr1::bind;
++using std::tr1::function;
++using namespace std::tr1::placeholders;
++
++} // namespace lambda {
++
++#endif // __STOUT_LAMBDA_HPP__
+diff --git a/include/mesos/stout/linkedhashmap.hpp b/include/mesos/stout/linkedhashmap.hpp
+new file mode 100644
+index 0000000..a27ec26
+--- /dev/null
++++ b/include/mesos/stout/linkedhashmap.hpp
+@@ -0,0 +1,92 @@
++#ifndef __STOUT_LINKEDHASHMAP_HPP__
++#define __STOUT_LINKEDHASHMAP_HPP__
++
++#include <list>
++#include <utility>
++
++#include <stout/hashmap.hpp>
++#include <stout/option.hpp>
++
++// Implementation of a hashmap that maintains the insertion order
++// of the keys. Note that re-insertion of a key (i.e., update)
++// doesn't update its insertion order.
++// TODO(vinod/bmahler): Consider extending from stout::hashmap and/or
++// having a compatible API with stout::hashmap.
++template <typename Key, typename Value>
++class LinkedHashMap
++{
++public:
++ typedef std::list<Key> list;
++ typedef hashmap<Key, std::pair<Value, typename list::iterator> > map;
++
++ Value& operator[] (const Key& key)
++ {
++ if (!values_.contains(key)) {
++ // Insert into the list and get the "pointer" into the list.
++ typename list::iterator i = keys_.insert(keys_.end(), key);
++ values_[key] = std::make_pair(Value(), i); // Store default value.
++ }
++ return values_[key].first;
++ }
++
++ Option<Value> get(const Key& key) const
++ {
++ if (values_.contains(key)) {
++ return values_.at(key).first;
++ }
++ return None();
++ }
++
++ bool contains(const Key& key) const
++ {
++ return values_.contains(key);
++ }
++
++ size_t erase(const Key& key)
++ {
++ if (values_.contains(key)) {
++ // Get the "pointer" into the list.
++ typename list::iterator i = values_[key].second;
++ keys_.erase(i);
++ return values_.erase(key);
++ }
++ return 0;
++ }
++
++ std::list<Key> keys() const
++ {
++ return keys_;
++ }
++
++ std::list<Value> values() const
++ {
++ std::list<Value> result;
++ foreach (const Key& key, keys_) {
++ result.push_back(values_.at(key).first);
++ }
++ return result;
++ }
++
++ size_t size() const
++ {
++ return keys_.size();
++ }
++
++ bool empty() const
++ {
++ return keys_.empty();
++ }
++
++ void clear()
++ {
++ values_.clear();
++ keys_.clear();
++ }
++
++private:
++ list keys_; // Keys ordered by the insertion order.
++ map values_; // Map of values and "pointers" to the linked list.
++};
++
++
++#endif // __STOUT_LINKEDHASHMAP_HPP__
+diff --git a/include/mesos/stout/multihashmap.hpp b/include/mesos/stout/multihashmap.hpp
+new file mode 100644
+index 0000000..10e49dc
+--- /dev/null
++++ b/include/mesos/stout/multihashmap.hpp
+@@ -0,0 +1,109 @@
++#ifndef __STOUT_MULTIHASHMAP_HPP__
++#define __STOUT_MULTIHASHMAP_HPP__
++
++#include <algorithm> // For find.
++#include <list>
++#include <set>
++#include <utility>
++
++#include <boost/unordered_map.hpp>
++
++
++// Implementation of a hash multimap via Boost's 'unordered_multimap'
++// but with a better interface. The rationale for creating this is
++// that the std::multimap interface is painful to use (requires lots
++// of iterator garbage, as well as the use of 'equal_range' which
++// makes for cluttered code).
++template <typename K, typename V>
++class multihashmap : public boost::unordered_multimap<K, V>
++{
++public:
++ void put(const K& key, const V& value);
++ std::list<V> get(const K& key) const;
++ std::set<K> keys() const;
++ bool remove(const K& key);
++ bool remove(const K& key, const V& value);
++ bool contains(const K& key) const;
++ bool contains(const K& key, const V& value) const;
++};
++
++
++template <typename K, typename V>
++void multihashmap<K, V>::put(const K& key, const V& value)
++{
++ boost::unordered_multimap<K, V>::insert(std::pair<K, V>(key, value));
++}
++
++
++template <typename K, typename V>
++std::list<V> multihashmap<K, V>::get(const K& key) const
++{
++ std::list<V> values; // Values to return.
++
++ std::pair<typename boost::unordered_multimap<K, V>::const_iterator,
++ typename boost::unordered_multimap<K, V>::const_iterator> range;
++
++ range = boost::unordered_multimap<K, V>::equal_range(key);
++
++ typename boost::unordered_multimap<K, V>::const_iterator i;
++ for (i = range.first; i != range.second; ++i) {
++ values.push_back(i->second);
++ }
++
++ return values;
++}
++
++
++template <typename K, typename V>
++std::set<K> multihashmap<K, V>::keys() const
++{
++ std::set<K> keys;
++ foreachkey (const K& key, *this) {
++ keys.insert(key);
++ }
++ return keys;
++}
++
++
++template <typename K, typename V>
++bool multihashmap<K, V>::remove(const K& key)
++{
++ return boost::unordered_multimap<K, V>::erase(key) > 0;
++}
++
++
++template <typename K, typename V>
++bool multihashmap<K, V>::remove(const K& key, const V& value)
++{
++ std::pair<typename boost::unordered_multimap<K, V>::iterator,
++ typename boost::unordered_multimap<K, V>::iterator> range;
++
++ range = boost::unordered_multimap<K, V>::equal_range(key);
++
++ typename boost::unordered_multimap<K, V>::iterator i;
++ for (i = range.first; i != range.second; ++i) {
++ if (i->second == value) {
++ boost::unordered_multimap<K, V>::erase(i);
++ return true;
++ }
++ }
++
++ return false;
++}
++
++
++template <typename K, typename V>
++bool multihashmap<K, V>::contains(const K& key) const
++{
++ return multihashmap<K, V>::count(key) > 0;
++}
++
++
++template <typename K, typename V>
++bool multihashmap<K, V>::contains(const K& key, const V& value) const
++{
++ const std::list<V>& values = get(key);
++ return std::find(values.begin(), values.end(), value) != values.end();
++}
++
++#endif // __STOUT_MULTIHASHMAP_HPP__
+diff --git a/include/mesos/stout/multimap.hpp b/include/mesos/stout/multimap.hpp
+new file mode 100644
+index 0000000..187ad79
+--- /dev/null
++++ b/include/mesos/stout/multimap.hpp
+@@ -0,0 +1,107 @@
++#ifndef __STOUT_MULTIMAP_HPP__
++#define __STOUT_MULTIMAP_HPP__
++
++#include <algorithm>
++#include <list>
++#include <map>
++#include <set>
++#include <utility>
++
++// Implementation of a multimap via std::multimap but with a better
++// interface. The rationale for creating this is that the
++// std::multimap interface is painful to use (requires lots of
++// iterator garbage, as well as the use of 'equal_range' which makes
++// for cluttered code).
++template <typename K, typename V>
++class Multimap : public std::multimap<K, V>
++{
++public:
++ void put(const K& key, const V& value);
++ std::list<V> get(const K& key) const;
++ std::set<K> keys() const;
++ bool remove(const K& key);
++ bool remove(const K& key, const V& value);
++ bool contains(const K& key) const;
++ bool contains(const K& key, const V& value) const;
++};
++
++
++template <typename K, typename V>
++void Multimap<K, V>::put(const K& key, const V& value)
++{
++ std::multimap<K, V>::insert(std::pair<K, V>(key, value));
++}
++
++
++template <typename K, typename V>
++std::list<V> Multimap<K, V>::get(const K& key) const
++{
++ std::list<V> values; // Values to return.
++
++ std::pair<typename std::multimap<K, V>::const_iterator,
++ typename std::multimap<K, V>::const_iterator> range;
++
++ range = std::multimap<K, V>::equal_range(key);
++
++ typename std::multimap<K, V>::const_iterator i;
++ for (i = range.first; i != range.second; ++i) {
++ values.push_back(i->second);
++ }
++
++ return values;
++}
++
++
++template <typename K, typename V>
++std::set<K> Multimap<K, V>::keys() const
++{
++ std::set<K> keys;
++ foreachkey (const K& key, *this) {
++ keys.insert(key);
++ }
++ return keys;
++}
++
++
++template <typename K, typename V>
++bool Multimap<K, V>::remove(const K& key)
++{
++ return std::multimap<K, V>::erase(key) > 0;
++}
++
++
++template <typename K, typename V>
++bool Multimap<K, V>::remove(const K& key, const V& value)
++{
++ std::pair<typename std::multimap<K, V>::iterator,
++ typename std::multimap<K, V>::iterator> range;
++
++ range = std::multimap<K, V>::equal_range(key);
++
++ typename std::multimap<K, V>::iterator i;
++ for (i = range.first; i != range.second; ++i) {
++ if (i->second == value) {
++ std::multimap<K, V>::erase(i);
++ return true;
++ }
++ }
++
++ return false;
++}
++
++
++template <typename K, typename V>
++bool Multimap<K, V>::contains(const K& key) const
++{
++ return std::multimap<K, V>::count(key) > 0;
++}
++
++
++template <typename K, typename V>
++bool Multimap<K, V>::contains(const K& key, const V& value) const
++{
++ const std::list<V>& values = get(key);
++ return std::find(values.begin(), values.end(), value) != values.end();
++}
++
++#endif // __STOUT_MULTIMAP_HPP__
+diff --git a/include/mesos/stout/net.hpp b/include/mesos/stout/net.hpp
+new file mode 100644
+index 0000000..d03de5a
+--- /dev/null
++++ b/include/mesos/stout/net.hpp
+@@ -0,0 +1,96 @@
++#ifndef __STOUT_NET_HPP__
++#define __STOUT_NET_HPP__
++
++#include <netdb.h>
++#include <stdio.h>
++
++#include <sys/param.h>
++#include <sys/socket.h>
++#include <sys/types.h>
++
++#include <curl/curl.h>
++
++#include <string>
++
++#include "error.hpp"
++#include "os.hpp"
++#include "try.hpp"
++
++
++// Network utilities.
++namespace net {
++
++// Returns the HTTP response code resulting from attempting to download the
++// specified HTTP or FTP URL into a file at the specified path.
++inline Try<int> download(const std::string& url, const std::string& path)
++{
++ Try<int> fd = os::open(
++ path, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IRWXO);
++
++ if (fd.isError()) {
++ return Error(fd.error());
++ }
++
++ curl_global_init(CURL_GLOBAL_ALL);
++ CURL* curl = curl_easy_init();
++
++ if (curl == NULL) {
++ curl_easy_cleanup(curl);
++ os::close(fd.get());
++ return Error("Failed to initialize libcurl");
++ }
++
++ curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
++ curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, NULL);
++
++ FILE* file = fdopen(fd.get(), "w");
++ if (file == NULL) {
++ return ErrnoError("Failed to open file handle of '" + path + "'");
++ }
++ curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);
++
++ CURLcode curlErrorCode = curl_easy_perform(curl);
++ if (curlErrorCode != 0) {
++ curl_easy_cleanup(curl);
++ fclose(file);
++ return Error(curl_easy_strerror(curlErrorCode));
++ }
++
++ long code;
++ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code);
++ curl_easy_cleanup(curl);
++
++ if (fclose(file) != 0) {
++ return ErrnoError("Failed to close file handle of '" + path + "'");
++ }
++
++ return Try<int>::some(code);
++}
++
++// Returns a Try of the hostname for the provided IP. If the hostname cannot
++// be resolved, then a string version of the IP address is returned.
++inline Try<std::string> getHostname(uint32_t ip)
++{
++ sockaddr_in addr;
++ memset(&addr, 0, sizeof(addr));
++ addr.sin_family = AF_INET;
++ addr.sin_addr.s_addr = ip;
++
++ char hostname[MAXHOSTNAMELEN];
++ if (getnameinfo(
++ (sockaddr*)&addr,
++ sizeof(addr),
++ hostname,
++ MAXHOSTNAMELEN,
++ NULL,
++ 0,
++ 0) != 0) {
++ return ErrnoError();
++ }
++
++ return std::string(hostname);
++}
++
++} // namespace net {
++
++#endif // __STOUT_NET_HPP__
+diff --git a/include/mesos/stout/none.hpp b/include/mesos/stout/none.hpp
+new file mode 100644
+index 0000000..ea8e0f5
+--- /dev/null
++++ b/include/mesos/stout/none.hpp
+@@ -0,0 +1,56 @@
++#ifndef __STOUT_NONE_HPP__
++#define __STOUT_NONE_HPP__
++
++#include "option.hpp"
++#include "result.hpp"
++
++// A "none" type that is implicitly convertible to an Option<T> and
++// Result<T> for any T (effectively "syntactic sugar" to make code
++// more readable). The implementation uses cast operators to perform
++// the conversions instead of adding constructors to Option/Result
++// directly. Performance shouldn't be an issue given that an instance
++// of None has no virtual functions and no fields.
++
++class None
++{
++public:
++ template <typename T>
++ operator Option<T> () const
++ {
++ return Option<T>::none();
++ }
++
++ // Give the compiler some help for nested Option<T>. For example,
++ // enable converting None to a Try<Option<T>>. Note that this will
++ // bind to the innermost Option<T>.
++ template <template <typename> class S, typename T>
++ operator S<Option<T> > () const
++ {
++ return S<Option<T> >(Option<T>::none());
++ }
++
++ template <typename T>
++ operator Result<T> () const
++ {
++ return Result<T>::none();
++ }
++
++ // Give the compiler some help for nested Result<T>. For example,
++ // enable converting None to a Try<Result<T>>. Note that this will
++ // bind to the innermost Result<T>.
++ template <template <typename> class S, typename T>
++ operator S<Result<T> > () const
++ {
++ return S<Result<T> >(Result<T>::none());
++ }
++
++ // Give the compiler some more help to disambiguate the above cast
++ // operators from Result<Option<T>>.
++ template <typename T>
++ operator Result<Option<T> > () const
++ {
++ return Result<Option<T> >::none();
++ }
++};
++
++#endif // __STOUT_NONE_HPP__
+diff --git a/include/mesos/stout/nothing.hpp b/include/mesos/stout/nothing.hpp
+new file mode 100644
+index 0000000..d0f925d
+--- /dev/null
++++ b/include/mesos/stout/nothing.hpp
+@@ -0,0 +1,6 @@
++#ifndef __STOUT_NOTHING_HPP__
++#define __STOUT_NOTHING_HPP__
++
++struct Nothing {};
++
++#endif // __STOUT_NOTHING_HPP__
+diff --git a/include/mesos/stout/numify.hpp b/include/mesos/stout/numify.hpp
+new file mode 100644
+index 0000000..d23e238
+--- /dev/null
++++ b/include/mesos/stout/numify.hpp
+@@ -0,0 +1,40 @@
++#ifndef __STOUT_NUMIFY_HPP__
++#define __STOUT_NUMIFY_HPP__
++
++#include <string>
++
++#include <boost/lexical_cast.hpp>
++
++#include "error.hpp"
++#include "none.hpp"
++#include "option.hpp"
++#include "result.hpp"
++#include "try.hpp"
++
++template <typename T>
++Try<T> numify(const std::string& s)
++{
++ try {
++ return boost::lexical_cast<T>(s);
++ } catch (const boost::bad_lexical_cast&) {
++ return Error("Failed to convert '" + s + "' to number");
++ }
++}
++
++
++template <typename T>
++Result<T> numify(const Option<std::string>& s)
++{
++ if (s.isSome()) {
++ Try<T> t = numify<T>(s.get());
++ if (t.isSome()) {
++ return t.get();
++ } else if (t.isError()) {
++ return Error(t.error());
++ }
++ }
++
++ return None();
++}
++
++#endif // __STOUT_NUMIFY_HPP__
+diff --git a/include/mesos/stout/option.hpp b/include/mesos/stout/option.hpp
+new file mode 100644
+index 0000000..42b75a5
+--- /dev/null
++++ b/include/mesos/stout/option.hpp
+@@ -0,0 +1,96 @@
++#ifndef __STOUT_OPTION_HPP__
++#define __STOUT_OPTION_HPP__
++
++#include <assert.h>
++
++#include <stout/result.hpp>
++
++template <typename T>
++class Option
++{
++public:
++ static Option<T> none()
++ {
++ return Option<T>(NONE);
++ }
++
++ static Option<T> some(const T& t)
++ {
++ return Option<T>(SOME, new T(t));
++ }
++
++ Option() : state(NONE), t(NULL) {}
++
++ Option(const T& _t) : state(SOME), t(new T(_t)) {}
++
++ Option(const Option<T>& that)
++ {
++ state = that.state;
++ if (that.t != NULL) {
++ t = new T(*that.t);
++ } else {
++ t = NULL;
++ }
++ }
++
++ ~Option()
++ {
++ delete t;
++ }
++
++ operator Result<T> () const
++ {
++ if (isNone()) {
++ return Result<T>::none();
++ }
++
++ return Result<T>::some(get());
++ }
++
++ Option<T>& operator = (const Option<T>& that)
++ {
++ if (this != &that) {
++ delete t;
++ state = that.state;
++ if (that.t != NULL) {
++ t = new T(*that.t);
++ } else {
++ t = NULL;
++ }
++ }
++
++ return *this;
++ }
++
++ bool operator == (const Option<T>& that) const
++ {
++ return (state == NONE && that.state == NONE) ||
++ (state == SOME && that.state == SOME && *t == *that.t);
++ }
++
++ bool operator != (const Option<T>& that) const
++ {
++ return !operator == (that);
++ }
++
++ bool isSome() const { return state == SOME; }
++ bool isNone() const { return state == NONE; }
++
++ T get() const { assert(state == SOME); return *t; }
++
++ T get(const T& _t) const { return state == NONE ? _t : *t; }
++
++private:
++ enum State {
++ SOME,
++ NONE,
++ };
++
++ Option(State _state, T* _t = NULL)
++ : state(_state), t(_t) {}
++
++ State state;
++ T* t;
++};
++
++#endif // __STOUT_OPTION_HPP__
+diff --git a/include/mesos/stout/os.hpp b/include/mesos/stout/os.hpp
+new file mode 100644
+index 0000000..544cf8c
+--- /dev/null
++++ b/include/mesos/stout/os.hpp
+@@ -0,0 +1,1056 @@
++#ifndef __STOUT_OS_HPP__
++#define __STOUT_OS_HPP__
++
++#ifdef __APPLE__
++#include <crt_externs.h> // For _NSGetEnviron().
++#endif
++#include <dirent.h>
++#include <errno.h>
++#include <fcntl.h>
++#include <fts.h>
++#include <glob.h>
++#include <libgen.h>
++#include <limits.h>
++#include <netdb.h>
++#include <pwd.h>
++#include <signal.h>
++#include <stddef.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <utime.h>
++
++#include <glog/logging.h>
++
++#ifdef __linux__
++#include <linux/version.h>
++#endif // __linux__
++
++#include <sys/stat.h>
++#include <sys/statvfs.h>
++#ifdef __linux__
++#include <sys/sysinfo.h>
++#endif // __linux__
++#include <sys/types.h>
++#include <sys/utsname.h>
++
++#include <list>
++#include <set>
++#include <sstream>
++#include <string>
++
++#include <stout/bytes.hpp>
++#include <stout/duration.hpp>
++#include <stout/error.hpp>
++#include <stout/foreach.hpp>
++#include <stout/none.hpp>
++#include <stout/nothing.hpp>
++#include <stout/option.hpp>
++#include <stout/path.hpp>
++#include <stout/result.hpp>
++#include <stout/strings.hpp>
++#include <stout/try.hpp>
++
++#include <stout/os/exists.hpp>
++#include <stout/os/fork.hpp>
++#include <stout/os/killtree.hpp>
++#ifdef __linux__
++#include <stout/os/linux.hpp>
++#endif // __linux__
++#include <stout/os/ls.hpp>
++#ifdef __APPLE__
++#include <stout/os/osx.hpp>
++#endif // __APPLE__
++#include <stout/os/pstree.hpp>
++#include <stout/os/read.hpp>
++#include <stout/os/sendfile.hpp>
++#include <stout/os/signals.hpp>
++#ifdef __APPLE__
++#include <stout/os/sysctl.hpp>
++#endif // __APPLE__
++
++#ifdef __APPLE__
++// Assigning the result pointer to ret silences an unused var warning.
++#define gethostbyname2_r(name, af, ret, buf, buflen, result, h_errnop) \
++ ({ (void)ret; *(result) = gethostbyname2(name, af); 0; })
++#endif // __APPLE__
++
++// Need to declare 'environ' pointer for non OS X platforms.
++#ifndef __APPLE__
++extern char** environ;
++#endif
++
++namespace os {
++
++inline char** environ()
++{
++ // Accessing the list of environment variables is platform-specific.
++ // On OS X, the 'environ' symbol isn't visible to shared libraries,
++ // so we must use the _NSGetEnviron() function (see 'man environ' on
++ // OS X). On other platforms, it's fine to access 'environ' from
++ // shared libraries.
++#ifdef __APPLE__
++ return *_NSGetEnviron();
++#else
++ return ::environ;
++#endif
++}
++
++
++// Checks if the specified key is in the environment variables.
++inline bool hasenv(const std::string& key)
++{
++ char* value = ::getenv(key.c_str());
++
++ return value != NULL;
++}
++
++// Looks in the environment variables for the specified key and
++// returns a string representation of it's value. If 'expected' is
++// true (default) and no environment variable matching key is found,
++// this function will exit the process.
++inline std::string getenv(const std::string& key, bool expected = true)
++{
++ char* value = ::getenv(key.c_str());
++
++ if (expected && value == NULL) {
++ LOG(FATAL) << "Expecting '" << key << "' in environment variables";
++ }
++
++ if (value != NULL) {
++ return std::string(value);
++ }
++
++ return std::string();
++}
++
++
++// Sets the value associated with the specified key in the set of
++// environment variables.
++inline void setenv(const std::string& key,
++ const std::string& value,
++ bool overwrite = true)
++{
++ ::setenv(key.c_str(), value.c_str(), overwrite ? 1 : 0);
++}
++
++
++// Unsets the value associated with the specified key in the set of
++// environment variables.
++inline void unsetenv(const std::string& key)
++{
++ ::unsetenv(key.c_str());
++}
++
++
++inline Try<bool> access(const std::string& path, int how)
++{
++ if (::access(path.c_str(), how) < 0) {
++ if (errno == EACCES) {
++ return false;
++ } else {
++ return ErrnoError();
++ }
++ }
++ return true;
++}
++
++
++inline Try<int> open(const std::string& path, int oflag, mode_t mode = 0)
++{
++ int fd = ::open(path.c_str(), oflag, mode);
++
++ if (fd < 0) {
++ return ErrnoError();
++ }
++
++ return fd;
++}
++
++
++inline Try<Nothing> close(int fd)
++{
++ if (::close(fd) != 0) {
++ return ErrnoError();
++ }
++
++ return Nothing();
++}
++
++
++inline Try<Nothing> cloexec(int fd)
++{
++ int flags = ::fcntl(fd, F_GETFD);
++
++ if (flags == -1) {
++ return ErrnoError();
++ }
++
++ if (::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1) {
++ return ErrnoError();
++ }
++
++ return Nothing();
++}
++
++
++inline Try<Nothing> nonblock(int fd)
++{
++ int flags = ::fcntl(fd, F_GETFL);
++
++ if (flags == -1) {
++ return ErrnoError();
++ }
++
++ if (::fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1) {
++ return ErrnoError();
++ }
++
++ return Nothing();
++}
++
++
++inline Try<bool> isNonblock(int fd)
++{
++ int flags = ::fcntl(fd, F_GETFL);
++
++ if (flags == -1) {
++ return ErrnoError();
++ }
++
++ return (flags & O_NONBLOCK) != 0;
++}
++
++
++// Sets the access and modification times of 'path' to the current time.
++inline Try<Nothing> utime(const std::string& path)
++{
++ if (::utime(path.c_str(), NULL) == -1) {
++ return ErrnoError();
++ }
++
++ return Nothing();
++}
++
++
++inline Try<Nothing> touch(const std::string& path)
++{
++ if (!exists(path)) {
++ Try<int> fd =
++ open(path, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IRWXO);
++
++ if (fd.isError()) {
++ return Error("Failed to open file: " + fd.error());
++ }
++
++ return close(fd.get());
++ }
++
++ // Update the access and modification times.
++ return utime(path);
++}
++
++
++// Creates a temporary file using the specified path template. The
++// template may be any path with _6_ `Xs' appended to it, for example
++// /tmp/temp.XXXXXX. The trailing `Xs' are replaced with a unique
++// alphanumeric combination.
++inline Try<std::string> mktemp(const std::string& path = "/tmp/XXXXXX")
++{
++ char* temp = new char[path.size() + 1];
++ int fd = ::mkstemp(::strcpy(temp, path.c_str()));
++
++ if (fd < 0) {
++ delete temp;
++ return ErrnoError();
++ }
++
++ // We ignore the return value of close(). This is because users
++ // calling this function are interested in the return value of
++ // mkstemp(). Also an unsuccessful close() doesn't affect the file.
++ os::close(fd);
++
++ std::string result(temp);
++ delete temp;
++ return result;
++}
++
++
++// Write out the string to the file at the current fd position.
++inline Try<Nothing> write(int fd, const std::string& message)
++{
++ size_t offset = 0;
++
++ while (offset < message.length()) {
++ ssize_t length =
++ ::write(fd, message.data() + offset, message.length() - offset);
++
++ if (length < 0) {
++ // TODO(benh): Handle a non-blocking fd? (EAGAIN, EWOULDBLOCK)
++ if (errno == EINTR) {
++ continue;
++ }
++ return ErrnoError();
++ }
++
++ offset += length;
++ }
++
++ return Nothing();
++}
++
++
++// A wrapper function that wraps the above write() with
++// open and closing the file.
++inline Try<Nothing> write(const std::string& path, const std::string& message)
++{
++ Try<int> fd = os::open(path, O_WRONLY | O_CREAT | O_TRUNC,
++ S_IRUSR | S_IWUSR | S_IRGRP | S_IRWXO);
++ if (fd.isError()) {
++ return ErrnoError("Failed to open file '" + path + "'");
++ }
++
++ Try<Nothing> result = write(fd.get(), message);
++
++ // We ignore the return value of close(). This is because users
++ // calling this function are interested in the return value of
++ // write(). Also an unsuccessful close() doesn't affect the write.
++ os::close(fd.get());
++
++ return result;
++}
++
++
++inline Try<Nothing> rm(const std::string& path)
++{
++ if (::remove(path.c_str()) != 0) {
++ return ErrnoError();
++ }
++
++ return Nothing();
++}
++
++
++inline Try<std::string> basename(const std::string& path)
++{
++ char* temp = new char[path.size() + 1];
++ char* result = ::basename(::strcpy(temp, path.c_str()));
++ if (result == NULL) {
++ delete[] temp;
++ return ErrnoError();
++ }
++
++ std::string s(result);
++ delete[] temp;
++ return s;
++}
++
++
++inline Try<std::string> dirname(const std::string& path)
++{
++ char* temp = new char[path.size() + 1];
++ char* result = ::dirname(::strcpy(temp, path.c_str()));
++ if (result == NULL) {
++ delete[] temp;
++ return ErrnoError();
++ }
++
++ std::string s(result);
++ delete[] temp;
++ return s;
++}
++
++
++inline Result<std::string> realpath(const std::string& path)
++{
++ char temp[PATH_MAX];
++ if (::realpath(path.c_str(), temp) == NULL) {
++ if (errno == ENOENT || errno == ENOTDIR) {
++ return None();
++ }
++ return ErrnoError();
++ }
++ return std::string(temp);
++}
++
++
++inline bool isdir(const std::string& path)
++{
++ struct stat s;
++
++ if (::stat(path.c_str(), &s) < 0) {
++ return false;
++ }
++ return S_ISDIR(s.st_mode);
++}
++
++
++inline bool isfile(const std::string& path)
++{
++ struct stat s;
++
++ if (::stat(path.c_str(), &s) < 0) {
++ return false;
++ }
++ return S_ISREG(s.st_mode);
++}
++
++
++inline bool islink(const std::string& path)
++{
++ struct stat s;
++
++ if (::lstat(path.c_str(), &s) < 0) {
++ return false;
++ }
++ return S_ISLNK(s.st_mode);
++}
++
++
++// TODO(benh): Put this in the 'paths' or 'files' or 'fs' namespace.
++inline Try<long> mtime(const std::string& path)
++{
++ struct stat s;
++
++ if (::lstat(path.c_str(), &s) < 0) {
++ return ErrnoError("Error invoking stat for '" + path + "'");
++ }
++
++ return s.st_mtime;
++}
++
++
++inline Try<Nothing> mkdir(const std::string& directory, bool recursive = true)
++{
++ if (!recursive) {
++ if (::mkdir(directory.c_str(), 0755) < 0) {
++ return ErrnoError();
++ }
++ } else {
++ std::vector<std::string> tokens = strings::tokenize(directory, "/");
++ std::string path = "";
++
++ // We got an absolute path, so keep the leading slash.
++ if (directory.find_first_of("/") == 0) {
++ path = "/";
++ }
++
++ foreach (const std::string& token, tokens) {
++ path += token;
++ if (::mkdir(path.c_str(), 0755) < 0 && errno != EEXIST) {
++ return ErrnoError();
++ }
++ path += "/";
++ }
++ }
++
++ return Nothing();
++}
++
++// Creates a temporary directory using the specified path
++// template. The template may be any path with _6_ `Xs' appended to
++// it, for example /tmp/temp.XXXXXX. The trailing `Xs' are replaced
++// with a unique alphanumeric combination.
++inline Try<std::string> mkdtemp(const std::string& path = "/tmp/XXXXXX")
++{
++ char* temp = new char[path.size() + 1];
++ if (::mkdtemp(::strcpy(temp, path.c_str())) != NULL) {
++ std::string result(temp);
++ delete[] temp;
++ return result;
++ } else {
++ delete[] temp;
++ return ErrnoError();
++ }
++}
++
++// By default, recursively deletes a directory akin to: 'rm -r'. If the
++// programmer sets recursive to false, it deletes a directory akin to: 'rmdir'.
++// Note that this function expects an absolute path.
++inline Try<Nothing> rmdir(const std::string& directory, bool recursive = true)
++{
++ if (!recursive) {
++ if (::rmdir(directory.c_str()) < 0) {
++ return ErrnoError();
++ }
++ } else {
++ char* paths[] = {const_cast<char*>(directory.c_str()), NULL};
++
++ FTS* tree = fts_open(paths, FTS_NOCHDIR, NULL);
++ if (tree == NULL) {
++ return ErrnoError();
++ }
++
++ FTSENT* node;
++ while ((node = fts_read(tree)) != NULL) {
++ switch (node->fts_info) {
++ case FTS_DP:
++ if (::rmdir(node->fts_path) < 0 && errno != ENOENT) {
++ return ErrnoError();
++ }
++ break;
++ case FTS_F:
++ case FTS_SL:
++ if (::unlink(node->fts_path) < 0 && errno != ENOENT) {
++ return ErrnoError();
++ }
++ break;
++ default:
++ break;
++ }
++ }
++
++ if (errno != 0) {
++ return ErrnoError();
++ }
++
++ if (fts_close(tree) < 0) {
++ return ErrnoError();
++ }
++ }
++
++ return Nothing();
++}
++
++
++inline int system(const std::string& command)
++{
++ return ::system(command.c_str());
++}
++
++
++// TODO(bmahler): Clean these bool functions to return Try<Nothing>.
++// Changes the specified path's user and group ownership to that of
++// the specified user..
++inline Try<Nothing> chown(
++ const std::string& user,
++ const std::string& path,
++ bool recursive = true)
++{
++ passwd* passwd;
++ if ((passwd = ::getpwnam(user.c_str())) == NULL) {
++ return ErrnoError("Failed to get user information for '" + user + "'");
++ }
++
++ if (recursive) {
++ // TODO(bmahler): Consider walking the file tree instead. We would need
++ // to be careful to not miss dotfiles.
++ std::string command = "chown -R " + stringify(passwd->pw_uid) + ':' +
++ stringify(passwd->pw_gid) + " '" + path + "'";
++
++ int status = os::system(command);
++ if (status != 0) {
++ return ErrnoError(
++ "Failed to execute '" + command +
++ "' (exit status: " + stringify(status) + ")");
++ }
++ } else {
++ if (::chown(path.c_str(), passwd->pw_uid, passwd->pw_gid) < 0) {
++ return ErrnoError();
++ }
++ }
++
++ return Nothing();
++}
++
++
++inline bool chmod(const std::string& path, int mode)
++{
++ if (::chmod(path.c_str(), mode) < 0) {
++ PLOG(ERROR) << "Failed to changed the mode of the path '" << path << "'";
++ return false;
++ }
++
++ return true;
++}
++
++
++inline bool chdir(const std::string& directory)
++{
++ if (::chdir(directory.c_str()) < 0) {
++ PLOG(ERROR) << "Failed to change directory";
++ return false;
++ }
++
++ return true;
++}
++
++
++inline bool su(const std::string& user)
++{
++ passwd* passwd;
++ if ((passwd = ::getpwnam(user.c_str())) == NULL) {
++ PLOG(ERROR) << "Failed to get user information for '"
++ << user << "', getpwnam";
++ return false;
++ }
++
++ if (::setgid(passwd->pw_gid) < 0) {
++ PLOG(ERROR) << "Failed to set group id, setgid";
++ return false;
++ }
++
++ if (::setuid(passwd->pw_uid) < 0) {
++ PLOG(ERROR) << "Failed to set user id, setuid";
++ return false;
++ }
++
++ return true;
++}
++
++
++inline std::string getcwd()
++{
++ size_t size = 100;
++
++ while (true) {
++ char* temp = new char[size];
++ if (::getcwd(temp, size) == temp) {
++ std::string result(temp);
++ delete[] temp;
++ return result;
++ } else {
++ if (errno != ERANGE) {
++ delete[] temp;
++ return std::string();
++ }
++ size *= 2;
++ delete[] temp;
++ }
++ }
++
++ return std::string();
++}
++
++
++// Return the list of file paths that match the given pattern by recursively
++// searching the given directory. A match is successful if the pattern is a
++// substring of the file name.
++// NOTE: Directory path should not end with '/'.
++// NOTE: Symbolic links are not followed.
++// TODO(vinod): Support regular expressions for pattern.
++// TODO(vinod): Consider using ftw or a non-recursive approach.
++inline Try<std::list<std::string> > find(
++ const std::string& directory,
++ const std::string& pattern)
++{
++ std::list<std::string> results;
++
++ if (!isdir(directory)) {
++ return Error("'" + directory + "' is not a directory");
++ }
++
++ foreach (const std::string& entry, ls(directory)) {
++ std::string path = path::join(directory, entry);
++ // If it's a directory, recurse.
++ if (isdir(path) && !islink(path)) {
++ Try<std::list<std::string> > matches = find(path, pattern);
++ if (matches.isError()) {
++ return matches;
++ }
++ foreach (const std::string& match, matches.get()) {
++ results.push_back(match);
++ }
++ } else {
++ if (entry.find(pattern) != std::string::npos) {
++ results.push_back(path); // Matched the file pattern!
++ }
++ }
++ }
++
++ return results;
++}
++
++
++inline std::string user()
++{
++ passwd* passwd;
++ if ((passwd = getpwuid(getuid())) == NULL) {
++ LOG(FATAL) << "Failed to get username information";
++ }
++
++ return passwd->pw_name;
++}
++
++
++inline Try<std::string> hostname()
++{
++ char host[512];
++
++ if (gethostname(host, sizeof(host)) < 0) {
++ return ErrnoError();
++ }
++
++ // Allocate temporary buffer for gethostbyname2_r.
++ size_t length = 1024;
++ char* temp = new char[length];
++
++ struct hostent he, *hep = NULL;
++ int result = 0;
++ int herrno = 0;
++
++ while ((result = gethostbyname2_r(host, AF_INET, &he, temp,
++ length, &hep, &herrno)) == ERANGE) {
++ // Enlarge the buffer.
++ delete[] temp;
++ length *= 2;
++ temp = new char[length];
++ }
++
++ if (result != 0 || hep == NULL) {
++ delete[] temp;
++ return Error(hstrerror(herrno));
++ }
++
++ std::string hostname = hep->h_name;
++ delete[] temp;
++ return hostname;
++}
++
++
++// Runs a shell command formatted with varargs and return the return value
++// of the command. Optionally, the output is returned via an argument.
++// TODO(vinod): Pass an istream object that can provide input to the command.
++inline Try<int> shell(std::ostream* os, const std::string& fmt, ...)
++{
++ va_list args;
++ va_start(args, fmt);
++
++ const Try<std::string>& cmdline = strings::internal::format(fmt, args);
++
++ va_end(args);
++
++ if (cmdline.isError()) {
++ return Error(cmdline.error());
++ }
++
++ FILE* file;
++
++ if ((file = popen(cmdline.get().c_str(), "r")) == NULL) {
++ return Error("Failed to run '" + cmdline.get() + "'");
++ }
++
++ char line[1024];
++ // NOTE(vinod): Ideally the if and while loops should be interchanged. But
++ // we get a broken pipe error if we don't read the output and simply close.
++ while (fgets(line, sizeof(line), file) != NULL) {
++ if (os != NULL) {
++ *os << line ;
++ }
++ }
++
++ if (ferror(file) != 0) {
++ ErrnoError error("Error reading output of '" + cmdline.get() + "'");
++ pclose(file); // Ignoring result since we already have an error.
++ return error;
++ }
++
++ int status;
++ if ((status = pclose(file)) == -1) {
++ return Error("Failed to get status of '" + cmdline.get() + "'");
++ }
++
++ return status;
++}
++
++
++// Suspends execution for the given duration.
++inline Try<Nothing> sleep(const Duration& duration)
++{
++ timespec remaining;
++ remaining.tv_sec = static_cast<long>(duration.secs());
++ remaining.tv_nsec =
++ static_cast<long>((duration - Seconds(remaining.tv_sec)).ns());
++
++ while (nanosleep(&remaining, &remaining) == -1) {
++ if (errno == EINTR) {
++ continue;
++ } else {
++ return ErrnoError();
++ }
++ }
++
++ return Nothing();
++}
++
++
++// Creates a tar 'archive' with gzip compression, of the given 'path'.
++inline Try<Nothing> tar(const std::string& path, const std::string& archive)
++{
++ Try<int> status =
++ shell(NULL, "tar -czf %s %s", archive.c_str(), path.c_str());
++
++ if (status.isError()) {
++ return Error("Failed to archive " + path + ": " + status.error());
++ } else if (status.get() != 0) {
++ return Error("Non-zero exit status when archiving " + path +
++ ": " + stringify(status.get()));
++ }
++
++ return Nothing();
++}
++
++
++// Returns the list of files that match the given (shell) pattern.
++inline Try<std::list<std::string> > glob(const std::string& pattern)
++{
++ glob_t g;
++ int status = ::glob(pattern.c_str(), GLOB_NOSORT, NULL, &g);
++
++ std::list<std::string> result;
++
++ if (status != 0) {
++ if (status == GLOB_NOMATCH) {
++ return result; // Empty list.
++ } else {
++ return ErrnoError();
++ }
++ }
++
++ for (size_t i = 0; i < g.gl_pathc; ++i) {
++ result.push_back(g.gl_pathv[i]);
++ }
++
++ globfree(&g); // Best-effort free of dynamically allocated memory.
++
++ return result;
++}
++
++
++// Returns the total number of cpus (cores).
++inline Try<long> cpus()
++{
++ long cpus = sysconf(_SC_NPROCESSORS_ONLN);
++
++ if (cpus < 0) {
++ return ErrnoError();
++ }
++ return cpus;
++}
++
++
++// Returns the total size of main memory.
++inline Try<Bytes> memory()
++{
++#ifdef __linux__
++ struct sysinfo info;
++ if (sysinfo(&info) != 0) {
++ return ErrnoError();
++ }
++# if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 23)
++ return Bytes(info.totalram * info.mem_unit);
++# else
++ return Bytes(info.totalram);
++# endif
++#elif defined __APPLE__
++ const Try<int64_t>& memory =
++ os::sysctl(CTL_HW, HW_MEMSIZE).integer();
++
++ if (memory.isError()) {
++ return Error(memory.error());
++ }
++ return Bytes(memory.get());
++#else
++ return Error("Cannot determine the size of main memory");
++#endif
++}
++
++
++// The structure returned by uname describing the currently running system.
++struct UTSInfo
++{
++ std::string sysname; // Operating system name (e.g. Linux).
++ std::string nodename; // Network name of this machine.
++ std::string release; // Release level of the operating system.
++ std::string version; // Version level of the operating system.
++ std::string machine; // Machine hardware platform.
++};
++
++
++// Return the system information.
++inline Try<UTSInfo> uname()
++{
++ struct utsname name;
++
++ if (::uname(&name) < 0) {
++ return ErrnoError();
++ }
++
++ UTSInfo info;
++ info.sysname = name.sysname;
++ info.nodename = name.nodename;
++ info.release = name.release;
++ info.version = name.version;
++ info.machine = name.machine;
++ return info;
++}
++
++
++// Return the operating system name (e.g. Linux).
++inline Try<std::string> sysname()
++{
++ Try<UTSInfo> info = uname();
++ if (info.isError()) {
++ return Error(info.error());
++ }
++
++ return info.get().sysname;
++}
++
++
++// The OS release level.
++struct Release
++{
++ int version;
++ int major;
++ int minor;
++};
++
++
++// Return the OS release numbers.
++inline Try<Release> release()
++{
++ Try<UTSInfo> info = uname();
++ if (info.isError()) {
++ return Error(info.error());
++ }
++
++ Release r;
++ if (::sscanf(
++ info.get().release.c_str(),
++ "%d.%d.%d",
++ &r.version,
++ &r.major,
++ &r.minor) != 3) {
++ return Error("Failed to parse: " + info.get().release);
++ }
++
++ return r;
++}
++
++
++inline Try<std::list<Process> > processes()
++{
++ const Try<std::set<pid_t> >& pids = os::pids();
++
++ if (pids.isError()) {
++ return Error(pids.error());
++ }
++
++ std::list<Process> result;
++ foreach (pid_t pid, pids.get()) {
++ const Result<Process>& process = os::process(pid);
++
++ // Ignore any processes that disappear.
++ if (process.isSome()) {
++ result.push_back(process.get());
++ }
++ }
++ return result;
++}
++
++
++inline Option<Process> process(
++ pid_t pid,
++ const std::list<Process>& processes)
++{
++ foreach (const Process& process, processes) {
++ if (process.pid == pid) {
++ return process;
++ }
++ }
++ return None();
++}
++
++
++inline std::set<pid_t> children(
++ pid_t pid,
++ const std::list<Process>& processes,
++ bool recursive = true)
++{
++ // Perform a breadth first search for descendants.
++ std::set<pid_t> descendants;
++ std::queue<pid_t> parents;
++ parents.push(pid);
++
++ do {
++ pid_t parent = parents.front();
++ parents.pop();
++
++ // Search for children of parent.
++ foreach (const Process& process, processes) {
++ if (process.parent == parent) {
++ // Have we seen this child yet?
++ if (descendants.insert(process.pid).second) {
++ parents.push(process.pid);
++ }
++ }
++ }
++ } while (recursive && !parents.empty());
++
++ return descendants;
++}
++
++
++inline Try<std::set<pid_t> > children(pid_t pid, bool recursive = true)
++{
++ const Try<std::list<Process> >& processes = os::processes();
++
++ if (processes.isError()) {
++ return Error(processes.error());
++ }
++
++ return children(pid, processes.get(), recursive);
++}
++
++
++// Overload of os::pids for filtering by groups and sessions.
++// A group / session id of 0 will fitler on the group / session ID
++// of the calling process.
++inline Try<std::set<pid_t> > pids(Option<pid_t> group, Option<pid_t> session)
++{
++ if (group.isNone() && session.isNone()) {
++ return os::pids();
++ } else if (group.isSome() && group.get() < 0) {
++ return Error("Invalid group");
++ } else if (session.isSome() && session.get() < 0) {
++ return Error("Invalid session");
++ }
++
++ const Try<std::list<Process> >& processes = os::processes();
++
++ if (processes.isError()) {
++ return Error(processes.error());
++ }
++
++ // Obtain the calling process group / session ID when 0 is provided.
++ if (group.isSome() && group.get() == 0) {
++ group = getpgid(0);
++ }
++ if (session.isSome() && session.get() == 0) {
++ session = getsid(0);
++ }
++
++ std::set<pid_t> result;
++ foreach (const Process& process, processes.get()) {
++ // Group AND Session (intersection).
++ if (group.isSome() && session.isSome()) {
++ if (group.get() == process.group &&
++ process.session.isSome() &&
++ session.get() == process.session.get()) {
++ result.insert(process.pid);
++ }
++ } else if (group.isSome() && group.get() == process.group) {
++ result.insert(process.pid);
++ } else if (session.isSome() && process.session.isSome() &&
++ session.get() == process.session.get()) {
++ result.insert(process.pid);
++ }
++ }
++
++ return result;
++}
++
++} // namespace os {
++
++#endif // __STOUT_OS_HPP__
+diff --git a/include/mesos/stout/os/exists.hpp b/include/mesos/stout/os/exists.hpp
+new file mode 100644
+index 0000000..0b30dbe
+--- /dev/null
++++ b/include/mesos/stout/os/exists.hpp
+@@ -0,0 +1,21 @@
++#ifndef __STOUT_OS_EXISTS_HPP__
++#define __STOUT_OS_EXISTS_HPP__
++
++#include <sys/stat.h>
++
++#include <string>
++
++namespace os {
++
++inline bool exists(const std::string& path)
++{
++ struct stat s;
++ if (::lstat(path.c_str(), &s) < 0) {
++ return false;
++ }
++ return true;
++}
++
++} // namespace os {
++
++#endif // __STOUT_OS_EXISTS_HPP__
+diff --git a/include/mesos/stout/os/fork.hpp b/include/mesos/stout/os/fork.hpp
+new file mode 100644
+index 0000000..838a5fe
+--- /dev/null
++++ b/include/mesos/stout/os/fork.hpp
+@@ -0,0 +1,433 @@
++#ifndef __STOUT_OS_FORK_HPP__
++#define __STOUT_OS_FORK_HPP__
++
++#include <fcntl.h>
++#include <unistd.h>
++
++#include <sys/mman.h>
++#include <sys/types.h>
++#include <sys/wait.h>
++
++#include <list>
++#include <set>
++#include <string>
++
++#include <tr1/memory>
++
++#include <stout/error.hpp>
++#include <stout/exit.hpp>
++#include <stout/foreach.hpp>
++#include <stout/option.hpp>
++#include <stout/os.hpp>
++#include <stout/stringify.hpp>
++#include <stout/try.hpp>
++
++#include <stout/os/process.hpp>
++
++// Abstractions around forking process trees. You can declare a
++// process tree "template" using 'Fork', 'Exec', and 'Wait'. For
++// example, to describe a simple "fork/exec" you can do:
++//
++// Fork f = Fork(Exec("sleep 10));
++//
++// The command passed to an 'Exec' is run via 'sh -c'. You can
++// construct more complicated templates via nesting, for example:
++//
++// Fork f =
++// Fork(None(),
++// Fork(Exec("echo 'grandchild 1'")),
++// Fork(None(),
++// Fork(Exec("echo 'great-grandchild'")),
++// Exec("echo 'grandchild 2'"))
++// Exec("echo 'child'"));
++//
++// Note that the first argument to 'Fork' here is an optional function
++// that can be invoked before forking any more children or executing a
++// command. THIS FUNCTION SHOULD BE ASYNC SIGNAL SAFE.
++//
++// To wait for children, you can use 'Wait' instead of 'Exec', for
++// example:
++//
++// Fork f =
++// Fork(None(),
++// Fork(Exec("echo 'grandchild 1'")),
++// Fork(Exec("echo 'grandchild 2'")),
++// Wait());
++//
++// You can also omit either an 'Exec' or a 'Wait' and the forked
++// process will just 'exit(0)'. For example, the following will cause
++// to processes to get reparented by 'init'.
++//
++// Fork f =
++// Fork(None(),
++// Fork(Exec("echo 'grandchild 1'")),
++// Fork(Exec("echo 'grandchild 2'")));
++//
++// A template can be instantiated by invoking the 'Fork' as a
++// functor. For example, using any of the templates above we can do:
++//
++// Try<ProcessTree> tree = f();
++//
++// It's important to note that the process tree returned represents
++// the instant in time after the forking has completed but before
++// 'Exec', 'Wait' or 'exit(0)' has occured (i.e., the process tree
++// will be complete).
++
++namespace os {
++
++// Forward declaration.
++inline Result<Process> process(pid_t);
++
++
++struct Exec
++{
++ Exec(const std::string& _command)
++ : command(_command) {}
++
++ const std::string command;
++};
++
++
++struct Wait {};
++
++
++struct Fork
++{
++ // -+- parent
++ Fork(const Option<void(*)(void)>& _function,
++ const Exec& _exec)
++ : function(_function),
++ exec(_exec) {}
++
++ Fork(const Exec& _exec) : exec(_exec) {}
++
++ // -+- parent
++ // \--- child
++ Fork(const Option<void(*)(void)>& _function,
++ const Fork& fork1)
++ : function(_function)
++ {
++ children.push_back(fork1);
++ }
++
++ Fork(const Option<void(*)(void)>& _function,
++ const Fork& fork1,
++ const Exec& _exec)
++ : function(_function),
++ exec(_exec)
++ {
++ children.push_back(fork1);
++ }
++
++ Fork(const Option<void(*)(void)>& _function,
++ const Fork& fork1,
++ const Wait& _wait)
++ : function(_function),
++ wait(_wait)
++ {
++ children.push_back(fork1);
++ }
++
++ // -+- parent
++ // |--- child
++ // \--- child
++ Fork(const Option<void(*)(void)>& _function,
++ const Fork& fork1,
++ const Fork& fork2)
++ : function(_function)
++ {
++ children.push_back(fork1);
++ children.push_back(fork2);
++ }
++
++ Fork(const Option<void(*)(void)>& _function,
++ const Fork& fork1,
++ const Fork& fork2,
++ const Exec& _exec)
++ : function(_function),
++ exec(_exec)
++ {
++ children.push_back(fork1);
++ children.push_back(fork2);
++ }
++
++ Fork(const Option<void(*)(void)>& _function,
++ const Fork& fork1,
++ const Fork& fork2,
++ const Wait& _wait)
++ : function(_function),
++ wait(_wait)
++ {
++ children.push_back(fork1);
++ children.push_back(fork2);
++ }
++
++ // -+- parent
++ // |--- child
++ // |--- child
++ // \--- child
++ Fork(const Option<void(*)(void)>& _function,
++ const Fork& fork1,
++ const Fork& fork2,
++ const Fork& fork3)
++ : function(_function)
++ {
++ children.push_back(fork1);
++ children.push_back(fork2);
++ children.push_back(fork3);
++ }
++
++ Fork(const Option<void(*)(void)>& _function,
++ const Fork& fork1,
++ const Fork& fork2,
++ const Fork& fork3,
++ const Exec& _exec)
++ : function(_function),
++ exec(_exec)
++ {
++ children.push_back(fork1);
++ children.push_back(fork2);
++ children.push_back(fork3);
++ }
++
++ Fork(const Option<void(*)(void)>& _function,
++ const Fork& fork1,
++ const Fork& fork2,
++ const Fork& fork3,
++ const Wait& _wait)
++ : function(_function),
++ wait(_wait)
++ {
++ children.push_back(fork1);
++ children.push_back(fork2);
++ children.push_back(fork3);
++ }
++
++private:
++ // Represents the "tree" of descendants where each node has a
++ // pointer (into shared memory) from which we can read the
++ // descendants process information as well as a vector of children.
++ struct Tree
++ {
++ // NOTE: This struct is stored in shared memory and thus cannot
++ // hold any pointers to heap allocated memory.
++ struct Memory {
++ pid_t pid;
++ pid_t parent;
++ pid_t group;
++ pid_t session;
++
++ bool set; // Has this been initialized?
++ };
++
++ std::tr1::shared_ptr<Memory> memory;
++ std::vector<Tree> children;
++ };
++
++ // We use shared memory to "share" the pids of forked descendants.
++ // The benefit of shared memory over pipes is that each forked
++ // process can read its descendants' pids leading to a simpler
++ // implementation (with pipes, only one reader can ever read the
++ // value from the pipe, forcing much more complicated coordination).
++ //
++ // Shared memory works like a file (in memory) that gets deleted by
++ // "unlinking" it, but it won't get completely deleted until all
++ // open file descriptors referencing it have been closed. Each
++ // forked process has the shared memory mapped into it as well as an
++ // open file descriptor, both of which should get cleaned up
++ // automagically when the process exits, but we use a special
++ // "deleter" (in combination with shared_ptr) in order to clean this
++ // stuff up when we are actually finished using the shared memory.
++ struct SharedMemoryDeleter
++ {
++ SharedMemoryDeleter(int _fd) : fd(_fd) {}
++
++ void operator () (Tree::Memory* process) const
++ {
++ if (munmap(process, sizeof(Tree::Memory)) == -1) {
++ perror("Failed to unmap memory");
++ abort();
++ }
++ if (::close(fd) == -1) {
++ perror("Failed to close shared memory file descriptor");
++ abort();
++ }
++ }
++
++ const int fd;
++ };
++
++ // Constructs a Tree (see above) from this fork template.
++ Try<Tree> prepare() const
++ {
++ static int forks = 0;
++
++ // Each "instance" of an instantiated Fork needs a unique name for
++ // creating shared memory.
++ int instance = __sync_fetch_and_add(&forks, 1);
++
++ std::string name =
++ "/stout-forks-" + stringify(getpid()) + stringify(instance);
++
++ int fd = shm_open(name.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
++
++ if (fd == -1) {
++ return ErrnoError("Failed to open a shared memory object");
++ }
++
++ if (ftruncate(fd, sizeof(Tree::Memory)) == -1) {
++ return ErrnoError("Failed to set size of shared memory object");
++ }
++
++ void* memory = mmap(
++ NULL,
++ sizeof(Tree::Memory),
++ PROT_READ | PROT_WRITE, MAP_SHARED,
++ fd,
++ 0);
++
++ if (memory == MAP_FAILED) {
++ return ErrnoError("Failed to map shared memory object");
++ }
++
++ if (shm_unlink(name.c_str()) == -1) {
++ return ErrnoError("Failed to unlink shared memory object");
++ }
++
++ SharedMemoryDeleter deleter(fd);
++
++ Tree tree;
++ tree.memory = std::tr1::shared_ptr<Tree::Memory>(
++ (Tree::Memory*) memory, deleter);
++ tree.memory->set = false;
++
++ for (size_t i = 0; i < children.size(); i++) {
++ Try<Tree> tree_ = children[i].prepare();
++ if (tree_.isError()) {
++ return Error(tree_.error());
++ }
++ tree.children.push_back(tree_.get());
++ }
++
++ return tree;
++ }
++
++ // Performs the fork, executes the function, recursively
++ // instantiates any children, and then executes/waits/exits.
++ pid_t instantiate(const Tree& tree) const
++ {
++ pid_t pid = ::fork();
++ if (pid > 0) {
++ return pid;
++ }
++
++ // Set the basic process information.
++ Tree::Memory process;
++ process.pid = getpid();
++ process.parent = getppid();
++ process.group = getpgid(0);
++ process.session = getsid(0);
++ process.set = true;
++
++ // Copy it into shared memory.
++ memcpy(tree.memory.get(), &process, sizeof(Tree::Memory));
++
++ // Execute the function, if any.
++ if (function.isSome()) {
++ function.get()();
++ }
++
++ // Fork the children, if any.
++ CHECK(children.size() == tree.children.size());
++ std::set<pid_t> pids;
++ for (size_t i = 0; i < children.size(); i++) {
++ pids.insert(children[i].instantiate(tree.children[i]));
++ }
++
++ // Execute or wait.
++ if (exec.isSome()) {
++ // Execute the command (via '/bin/sh -c command').
++ const char* command = exec.get().command.c_str();
++ execl("/bin/sh", "sh", "-c", command, (char*) NULL);
++ EXIT(1) << "Failed to execute '" << command << "': " << strerror(errno);
++ } else if (wait.isSome()) {
++ foreach (pid_t pid, pids) {
++ // TODO(benh): Check for signal interruption or other errors.
++ waitpid(pid, NULL, 0);
++ }
++ }
++
++ exit(0);
++ return -1;
++ }
++
++ // Waits for all of the descendant processes in the tree to update
++ // their pids and constructs a ProcessTree using the Tree::Memory
++ // information from shared memory.
++ static Try<ProcessTree> coordinate(const Tree& tree)
++ {
++ // Wait for the forked process.
++ // TODO(benh): Don't wait forever?
++ while (!tree.memory->set) {
++ // Make sure we don't keep reading the value from a register.
++ __sync_synchronize();
++ }
++
++ // All processes in the returned ProcessTree will have the
++ // command-line of the top level process, since we construct the
++ // tree using post-fork pre-exec information. So, we'll grab the
++ // command of the current process here.
++ Result<Process> self = os::process(getpid());
++
++ Process process = Process(
++ tree.memory->pid,
++ tree.memory->parent,
++ tree.memory->group,
++ tree.memory->session,
++ None(),
++ None(),
++ None(),
++ self.isSome() ? self.get().command : "",
++ false);
++
++ std::list<ProcessTree> children;
++ for (size_t i = 0; i < tree.children.size(); i++) {
++ Try<ProcessTree> child = coordinate(tree.children[i]);
++ if (child.isError()) {
++ return Error(child.error());
++ }
++ children.push_back(child.get());
++ }
++
++ return ProcessTree(process, children);
++ }
++
++public:
++ // Prepares and instantiates the process tree.
++ Try<ProcessTree> operator () () const
++ {
++ Try<Tree> tree = prepare();
++
++ if (tree.isError()) {
++ return Error(tree.error());
++ }
++
++ Try<pid_t> pid = instantiate(tree.get());
++
++ if (pid.isError()) {
++ return Error(pid.error());
++ }
++
++ return coordinate(tree.get());
++ }
++
++private:
++ Option<void(*)(void)> function;
++ Option<const Exec> exec;
++ Option<const Wait> wait;
++ std::vector<Fork> children;
++};
++
++} // namespace os {
++
++#endif // __STOUT_OS_FORK_HPP__
+diff --git a/include/mesos/stout/os/killtree.hpp b/include/mesos/stout/os/killtree.hpp
+new file mode 100644
+index 0000000..25e9937
+--- /dev/null
++++ b/include/mesos/stout/os/killtree.hpp
+@@ -0,0 +1,186 @@
++#ifndef __STOUT_OS_KILLTREE_HPP__
++#define __STOUT_OS_KILLTREE_HPP__
++
++#include <dirent.h>
++#include <stdlib.h>
++#include <unistd.h>
++
++#include <list>
++#include <ostream>
++#include <queue>
++#include <set>
++#include <sstream>
++#include <string>
++
++#include <stout/check.hpp>
++#include <stout/os.hpp>
++#include <stout/stringify.hpp>
++
++#include <stout/os/pstree.hpp>
++
++namespace os {
++
++// Forward declarations from os.hpp.
++inline std::set<pid_t> children(pid_t, const std::list<Process>&, bool);
++inline Option<Process> process(pid_t, const std::list<Process>&);
++
++
++// Sends a signal to a process tree rooted at the specified pid.
++// If groups is true, this also sends the signal to all encountered
++// process groups.
++// If sessions is true, this also sends the signal to all encountered
++// process sessions.
++// Note that processes of the group and session of the parent of the
++// root process is not included unless they are part of the root
++// process tree.
++// Returns the process trees that were succesfully or unsuccessfully
++// signaled. Note that the process trees can be stringified.
++inline Try<std::list<ProcessTree> > killtree(
++ pid_t pid,
++ int signal,
++ bool groups = false,
++ bool sessions = false)
++{
++ Try<std::list<Process> > processes = os::processes();
++
++ if (processes.isError()) {
++ return Error(processes.error());
++ }
++
++ Result<Process> process = os::process(pid, processes.get());
++
++ if (process.isNone()) {
++ return Error("Failed to find process " + stringify(pid));
++ }
++
++ struct {
++ std::set<pid_t> pids;
++ std::set<pid_t> groups;
++ std::set<pid_t> sessions;
++ std::list<Process> processes;
++ } visited;
++
++ // If we are following groups and/or sessions then we try and make
++ // the group and session of the parent process "already visited" so
++ // that we don't kill "up the tree".
++ if (groups || sessions) {
++ Option<Process> parent =
++ os::process(process.get().parent, processes.get());
++
++ if (parent.isSome()) {
++ if (groups) {
++ visited.groups.insert(parent.get().group);
++ }
++ if (sessions && parent.get().session.isSome()) {
++ visited.sessions.insert(parent.get().session.get());
++ }
++ }
++ }
++
++ std::queue<pid_t> queue;
++ queue.push(pid);
++
++ while (!queue.empty()) {
++ pid_t pid = queue.front();
++ queue.pop();
++
++ if (visited.pids.count(pid) != 0) {
++ continue;
++ }
++
++ // Make sure this process still exists.
++ process = os::process(pid);
++
++ if (process.isError()) {
++ return Error(process.error());
++ } else if (process.isNone()) {
++ continue;
++ }
++
++ // Stop the process to keep it from forking while we are killing
++ // it since a forked child might get re-parented by init and
++ // become impossible to find.
++ kill(pid, SIGSTOP);
++
++ visited.pids.insert(pid);
++ visited.processes.push_back(process.get());
++
++ // Now refresh the process list knowing that the current process
++ // can't fork any more children (since it's stopped).
++ processes = os::processes();
++
++ if (processes.isError()) {
++ return Error(processes.error());
++ }
++
++ // Enqueue the children for visiting.
++ foreach (pid_t child, os::children(pid, processes.get(), false)) {
++ queue.push(child);
++ }
++
++ // Now "visit" the group and/or session of the current process.
++ if (groups) {
++ pid_t group = process.get().group;
++ if (visited.groups.count(group) == 0) {
++ foreach (const Process& process, processes.get()) {
++ if (process.group == group) {
++ queue.push(process.pid);
++ }
++ }
++ visited.groups.insert(group);
++ }
++ }
++
++ // If we do not have a session for the process, it's likely
++ // because the process is a zombie on OS X. This implies it has
++ // not been reaped and thus is located somewhere in the tree we
++ // are trying to kill. Therefore, we should discover it from our
++ // tree traversal, or through its group (which is always present).
++ if (sessions && process.get().session.isSome()) {
++ pid_t session = process.get().session.get();
++ if (visited.sessions.count(session) == 0) {
++ foreach (const Process& process, processes.get()) {
++ if (process.session.isSome() && process.session.get() == session) {
++ queue.push(process.pid);
++ }
++ }
++ visited.sessions.insert(session);
++ }
++ }
++ }
++
++ // Now that all processes are stopped, we send the signal.
++ foreach (pid_t pid, visited.pids) {
++ kill(pid, signal);
++ }
++
++ // There is a concern that even though some process is stopped,
++ // sending a signal to any of it's children may cause a SIGCLD to
++ // be delivered to it which wakes it up (or any other signal maybe
++ // delivered). However, from the Open Group standards on "Signal
++ // Concepts":
++ //
++ // "While a process is stopped, any additional signals that are
++ // sent to the process shall not be delivered until the process
++ // is continued, except SIGKILL which always terminates the
++ // receiving process."
++ //
++ // In practice, this is not what has been witnessed. Rather, a
++ // process that has been stopped will respond to SIGTERM, SIGINT,
++ // etc. That being said, we still continue the process below in the
++ // event that it doesn't terminate from the sending signal but it
++ // also doesn't get continued (as per the specifications above).
++
++ // Try and continue the processes in case the signal is
++ // non-terminating but doesn't continue the process.
++ foreach (pid_t pid, visited.pids) {
++ kill(pid, SIGCONT);
++ }
++
++ // Return the process trees representing the visited pids.
++ return pstrees(visited.pids, visited.processes);
++}
++
++} // namespace os {
++
++#endif // __STOUT_OS_KILLTREE_HPP__
+diff --git a/include/mesos/stout/os/linux.hpp b/include/mesos/stout/os/linux.hpp
+new file mode 100644
+index 0000000..25d5903
+--- /dev/null
++++ b/include/mesos/stout/os/linux.hpp
+@@ -0,0 +1,87 @@
++#ifndef __STOUT_OS_LINUX_HPP__
++#define __STOUT_OS_LINUX_HPP__
++
++// This file contains Linux-only OS utilities.
++#ifndef __linux__
++#error "stout/os/linux.hpp is only available on Linux systems."
++#endif
++
++#include <sys/types.h> // For pid_t.
++
++#include <list>
++#include <queue>
++#include <set>
++
++#include <stout/error.hpp>
++#include <stout/foreach.hpp>
++#include <stout/option.hpp>
++#include <stout/proc.hpp>
++#include <stout/result.hpp>
++#include <stout/try.hpp>
++
++#include <stout/os/process.hpp>
++
++namespace os {
++
++inline Result<Process> process(pid_t pid)
++{
++ // Page size, used for memory accounting.
++ // NOTE: This is more portable than using getpagesize().
++ static const long pageSize = sysconf(_SC_PAGESIZE);
++ if (pageSize <= 0) {
++ return Error("Failed to get sysconf(_SC_PAGESIZE)");
++ }
++
++ // Number of clock ticks per second, used for cpu accounting.
++ static const long ticks = sysconf(_SC_CLK_TCK);
++ if (ticks <= 0) {
++ return Error("Failed to get sysconf(_SC_CLK_TCK)");
++ }
++
++ const Result<proc::ProcessStatus>& status = proc::status(pid);
++
++ if (status.isError()) {
++ return Error(status.error());
++ }
++
++ if (status.isNone()) {
++ return None();
++ }
++
++ // There are known bugs with invalid utime / stime values coming
++ // from /proc/<pid>/stat on some Linux systems.
++ // See the following thread for details:
++ // http://mail-archives.apache.org/mod_mbox/incubator-mesos-dev/
++ // 201307.mbox/%3CCA+2n2er-Nemh0CsKLbHRkaHd=YCrNt17NLUPM2=TtEfsKOw4
++ // Rg at mail.gmail.com%3E
++ // These are similar reports:
++ // http://lkml.indiana.edu/hypermail/linux/kernel/1207.1/01388.html
++ // https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1023214
++ Try<Duration> utime = Duration::create(status.get().utime / (double) ticks);
++ Try<Duration> stime = Duration::create(status.get().stime / (double) ticks);
++
++ // The command line from 'status.get().comm' is only "arg0" from
++ // "argv" (i.e., the canonical executable name). To get the entire
++ // command line we grab '/proc/[pid]/cmdline'.
++ Result<std::string> cmdline = proc::cmdline(pid);
++
++ return Process(status.get().pid,
++ status.get().ppid,
++ status.get().pgrp,
++ status.get().session,
++ Bytes(status.get().rss * pageSize),
++ utime.isSome() ? utime.get() : Option<Duration>::none(),
++ stime.isSome() ? stime.get() : Option<Duration>::none(),
++ cmdline.isSome() ? cmdline.get() : status.get().comm,
++ status.get().state == 'Z');
++}
++
++
++inline Try<std::set<pid_t> > pids()
++{
++ return proc::pids();
++}
++
++} // namespace os {
++
++#endif // __STOUT_OS_LINUX_HPP__
+diff --git a/include/mesos/stout/os/ls.hpp b/include/mesos/stout/os/ls.hpp
+new file mode 100644
+index 0000000..7637a0d
+--- /dev/null
++++ b/include/mesos/stout/os/ls.hpp
+@@ -0,0 +1,66 @@
++#ifndef __STOUT_OS_LS_HPP__
++#define __STOUT_OS_LS_HPP__
++
++#include <dirent.h>
++#include <stdlib.h>
++#include <unistd.h>
++
++#include <list>
++#include <string>
++
++namespace os {
++
++// TODO(bmahler): Wrap this with a Try.
++inline std::list<std::string> ls(const std::string& directory)
++{
++ DIR* dir = opendir(directory.c_str());
++
++ if (dir == NULL) {
++ return std::list<std::string>();
++ }
++
++ // Calculate the size for a "directory entry".
++ long name_max = fpathconf(dirfd(dir), _PC_NAME_MAX);
++
++ // If we don't get a valid size, check NAME_MAX, but fall back on
++ // 255 in the worst case ... Danger, Will Robinson!
++ if (name_max == -1) {
++ name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
++ }
++
++ size_t name_end = (size_t) offsetof(dirent, d_name) + name_max + 1;
++
++ size_t size = (name_end > sizeof(dirent) ? name_end : sizeof(dirent));
++
++ dirent* temp = (dirent*) malloc(size);
++
++ if (temp == NULL) {
++ free(temp);
++ closedir(dir);
++ return std::list<std::string>();
++ }
++
++ std::list<std::string> result;
++ struct dirent* entry;
++ int error;
++
++ while ((error = readdir_r(dir, temp, &entry)) == 0 && entry != NULL) {
++ if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
++ continue;
++ }
++ result.push_back(entry->d_name);
++ }
++
++ free(temp);
++ closedir(dir);
++
++ if (error != 0) {
++ return std::list<std::string>();
++ }
++
++ return result;
++}
++
++} // namespace os {
++
++#endif // __STOUT_OS_LS_HPP__
+diff --git a/include/mesos/stout/os/osx.hpp b/include/mesos/stout/os/osx.hpp
+new file mode 100644
+index 0000000..7d02566
+--- /dev/null
++++ b/include/mesos/stout/os/osx.hpp
+@@ -0,0 +1,165 @@
++#ifndef __STOUT_OS_OSX_HPP__
++#define __STOUT_OS_OSX_HPP__
++
++// This file contains OSX-only OS utilities.
++#ifndef __APPLE__
++#error "stout/os/osx.hpp is only available on OSX systems."
++#endif
++
++#include <libproc.h>
++
++#include <sys/sysctl.h>
++#include <sys/types.h> // For pid_t.
++
++#include <queue>
++#include <set>
++
++#include <stout/error.hpp>
++#include <stout/none.hpp>
++#include <stout/strings.hpp>
++
++#include <stout/os/process.hpp>
++#include <stout/os/sysctl.hpp>
++
++namespace os {
++
++inline Result<Process> process(pid_t pid)
++{
++ const Try<std::vector<kinfo_proc> >& processes =
++ os::sysctl(CTL_KERN, KERN_PROC, KERN_PROC_PID, pid).table(1);
++
++ if (processes.isError()) {
++ return Error("Failed to get process via sysctl: " + processes.error());
++ } else if (processes.get().size() != 1) {
++ return None();
++ }
++
++ const kinfo_proc process = processes.get()[0];
++
++ // The command line from 'process.kp_proc.p_comm' only includes the
++ // first 16 characters from "arg0" (i.e., the canonical executable
++ // name). We can try to get "argv" via some sysctl magic. This first
++ // requires determining "argc" via KERN_PROCARGS2 followed by the
++ // actual arguments via KERN_PROCARGS. This is still insufficient
++ // with insufficient privilege (e.g., not being root). If we were
++ // only interested in the "executable path" (i.e., the first
++ // argument to 'exec' but none of the arguments) we could use
++ // proc_pidpath() instead.
++ Option<std::string> command = None();
++
++#ifdef KERN_PROCARGS2
++ // Looking at the source code of XNU (the Darwin kernel for OS X:
++ // www.opensource.apple.com/source/xnu/xnu-1699.24.23/bsd/kern/kern_sysctl.c),
++ // it appears as though KERN_PROCARGS2 writes 'argc' as the first
++ // word of the returned bytes.
++ Try<std::string> args = os::sysctl(CTL_KERN, KERN_PROCARGS2, pid).string();
++
++ if (args.isSome()) {
++ int argc = *((int*) args.get().data());
++
++ if (argc > 0) {
++ // Now grab the arguments.
++ args = os::sysctl(CTL_KERN, KERN_PROCARGS, pid).string();
++
++ if (args.isSome()) {
++ // At this point 'args' contains the parameters to 'exec'
++ // delimited by null bytes, i.e., "executable path", then
++ // "arg0" (the canonical executable name), then "arg1", then
++ // "arg2", etc. Sometimes there are no arguments (argc = 1) so
++ // all we care about is the "executable path", but when there
++ // are arguments we grab "arg0" and on assuming that "arg0"
++ // really is the canonical executable name.
++
++ // Tokenize the args by the null byte ('\0').
++ std::vector<std::string> tokens =
++ strings::tokenize(args.get(), std::string(1, '\0'));
++
++ if (!tokens.empty()) {
++ if (argc == 1) {
++ // When there are no arguments, all we care about is the
++ // "executable path".
++ command = tokens[0];
++ } else if (argc > 1) {
++ // When there are arguments, we skip the "executable path"
++ // and just grab "arg0" -> "argN", assuming "arg0" is the
++ // canonical executable name. In the case that we didn't
++ // get enough tokens back from KERN_PROCARGS the following
++ // code will end up just keeping 'command' None (i.e.,
++ // tokens.size() will be <= 0).
++ tokens.erase(tokens.begin()); // Remove path.
++ tokens.erase(tokens.begin() + argc, tokens.end());
++ if (tokens.size() > 0) {
++ command = strings::join(" ", tokens);
++ }
++ }
++ }
++ }
++ }
++ }
++#endif
++
++ // We also use proc_pidinfo() to get memory and CPU usage.
++ // NOTE: There are several pitfalls to using proc_pidinfo().
++ // In particular:
++ // -This will not work for many root processes.
++ // -This may not work for processes owned by other users.
++ // -However, this always works for processes owned by the same user.
++ // This beats using task_for_pid(), which only works for the same pid.
++ // For further discussion around these issues,
++ // see: http://code.google.com/p/psutil/issues/detail?id=297
++ proc_taskinfo task;
++ int size = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &task, sizeof(task));
++
++ // It appears that zombie processes on OS X do not have sessions and
++ // result in ESRCH.
++ int session = getsid(pid);
++
++ if (size != sizeof(task)) {
++ return Process(process.kp_proc.p_pid,
++ process.kp_eproc.e_ppid,
++ process.kp_eproc.e_pgid,
++ session > 0 ? session : Option<pid_t>::none(),
++ None(),
++ None(),
++ None(),
++ command.get(std::string(process.kp_proc.p_comm)),
++ process.kp_proc.p_stat & SZOMB);
++ } else {
++ return Process(process.kp_proc.p_pid,
++ process.kp_eproc.e_ppid,
++ process.kp_eproc.e_pgid,
++ session > 0 ? session : Option<pid_t>::none(),
++ Bytes(task.pti_resident_size),
++ Nanoseconds(task.pti_total_user),
++ Nanoseconds(task.pti_total_system),
++ command.get(std::string(process.kp_proc.p_comm)),
++ process.kp_proc.p_stat & SZOMB);
++ }
++}
++
++
++inline Try<std::set<pid_t> > pids()
++{
++ const Try<int>& maxproc = os::sysctl(CTL_KERN, KERN_MAXPROC).integer();
++
++ if (maxproc.isError()) {
++ return Error(maxproc.error());
++ }
++
++ const Try<std::vector<kinfo_proc> >& processes =
++ os::sysctl(CTL_KERN, KERN_PROC, KERN_PROC_ALL).table(maxproc.get());
++
++ if (processes.isError()) {
++ return Error(processes.error());
++ }
++
++ std::set<pid_t> result;
++ foreach (const kinfo_proc& process, processes.get()) {
++ result.insert(process.kp_proc.p_pid);
++ }
++ return result;
++}
++
++} // namespace os {
++
++#endif // __STOUT_OS_OSX_HPP__
+diff --git a/include/mesos/stout/os/process.hpp b/include/mesos/stout/os/process.hpp
+new file mode 100644
+index 0000000..d754601
+--- /dev/null
++++ b/include/mesos/stout/os/process.hpp
+@@ -0,0 +1,166 @@
++#ifndef __STOUT_OS_PROCESS_HPP__
++#define __STOUT_OS_PROCESS_HPP__
++
++#include <sys/types.h> // For pid_t.
++
++#include <list>
++#include <ostream>
++#include <sstream>
++#include <string>
++
++#include <stout/bytes.hpp>
++#include <stout/duration.hpp>
++#include <stout/none.hpp>
++#include <stout/option.hpp>
++#include <stout/strings.hpp>
++
++namespace os {
++
++struct Process
++{
++ Process(pid_t _pid,
++ pid_t _parent,
++ pid_t _group,
++ const Option<pid_t>& _session,
++ const Option<Bytes>& _rss,
++ const Option<Duration>& _utime,
++ const Option<Duration>& _stime,
++ const std::string& _command,
++ bool _zombie)
++ : pid(_pid),
++ parent(_parent),
++ group(_group),
++ session(_session),
++ rss(_rss),
++ utime(_utime),
++ stime(_stime),
++ command(_command),
++ zombie(_zombie) {}
++
++ const pid_t pid;
++ const pid_t parent;
++ const pid_t group;
++ const Option<pid_t> session;
++ const Option<Bytes> rss;
++ const Option<Duration> utime;
++ const Option<Duration> stime;
++ const std::string command;
++ const bool zombie;
++
++ // TODO(bmahler): Add additional data as needed.
++
++ bool operator < (const Process& p) const { return pid < p.pid; }
++ bool operator <= (const Process& p) const { return pid <= p.pid; }
++ bool operator > (const Process& p) const { return pid > p.pid; }
++ bool operator >= (const Process& p) const { return pid >= p.pid; }
++ bool operator == (const Process& p) const { return pid == p.pid; }
++ bool operator != (const Process& p) const { return pid != p.pid; }
++};
++
++
++class ProcessTree
++{
++public:
++ // Returns a process subtree rooted at the specified PID, or none if
++ // the specified pid could not be found in this process tree.
++ Option<ProcessTree> find(pid_t pid) const
++ {
++ if (process.pid == pid) {
++ return *this;
++ }
++
++ foreach (const ProcessTree& tree, children) {
++ Option<ProcessTree> option = tree.find(pid);
++ if (option.isSome()) {
++ return option;
++ }
++ }
++
++ return None();
++ }
++
++ // Checks if the specified pid is contained in this process tree.
++ bool contains(pid_t pid) const
++ {
++ return find(pid).isSome();
++ }
++
++ operator Process () const
++ {
++ return process;
++ }
++
++ operator pid_t () const
++ {
++ return process.pid;
++ }
++
++ const Process process;
++ const std::list<ProcessTree> children;
++
++private:
++ friend struct Fork;
++ friend Try<ProcessTree> pstree(pid_t, const std::list<Process>&);
++
++ ProcessTree(
++ const Process& _process,
++ const std::list<ProcessTree>& _children)
++ : process(_process),
++ children(_children) {}
++};
++
++
++inline std::ostream& operator << (
++ std::ostream& stream,
++ const ProcessTree& tree)
++{
++ if (tree.children.empty()) {
++ stream << "--- " << tree.process.pid << " ";
++ if (tree.process.zombie) {
++ stream << "(" << tree.process.command << ")";
++ } else {
++ stream << tree.process.command;
++ }
++ } else {
++ stream << "-+- " << tree.process.pid << " ";
++ if (tree.process.zombie) {
++ stream << "(" << tree.process.command << ")";
++ } else {
++ stream << tree.process.command;
++ }
++ size_t size = tree.children.size();
++ foreach (const ProcessTree& child, tree.children) {
++ std::ostringstream out;
++ out << child;
++ stream << "\n";
++ if (--size != 0) {
++ stream << " |" << strings::replace(out.str(), "\n", "\n |");
++ } else {
++ stream << " \\" << strings::replace(out.str(), "\n", "\n ");
++ }
++ }
++ }
++ return stream;
++}
++
++} // namespace os {
++
++
++// An overload of stringify for printing a list of process trees
++// (since printing a process tree is rather particular).
++inline std::string stringify(const std::list<os::ProcessTree>& list)
++{
++ std::ostringstream out;
++ out << "[ " << std::endl;
++ std::list<os::ProcessTree>::const_iterator iterator = list.begin();
++ while (iterator != list.end()) {
++ out << stringify(*iterator);
++ if (++iterator != list.end()) {
++ out << std::endl << std::endl;
++ }
++ }
++ out << std::endl << "]";
++ return out.str();
++}
++
++#endif // __STOUT_OS_PROCESS_HPP__
+diff --git a/include/mesos/stout/os/pstree.hpp b/include/mesos/stout/os/pstree.hpp
+new file mode 100644
+index 0000000..4637e68
+--- /dev/null
++++ b/include/mesos/stout/os/pstree.hpp
+@@ -0,0 +1,119 @@
++#ifndef __STOUT_OS_PSTREE_HPP__
++#define __STOUT_OS_PSTREE_HPP__
++
++#include <list>
++#include <set>
++
++#include <stout/error.hpp>
++#include <stout/foreach.hpp>
++#include <stout/none.hpp>
++#include <stout/option.hpp>
++#include <stout/os.hpp>
++#include <stout/stringify.hpp>
++#include <stout/try.hpp>
++
++#include <stout/os/process.hpp>
++
++namespace os {
++
++// Forward declaration.
++inline Try<std::list<Process> > processes();
++
++
++// Returns a process tree rooted at the specified pid using the
++// specified list of processes (or an error if one occurs).
++inline Try<ProcessTree> pstree(
++ pid_t pid,
++ const std::list<Process>& processes)
++{
++ std::list<ProcessTree> children;
++ foreach (const Process& process, processes) {
++ if (process.parent == pid) {
++ Try<ProcessTree> tree = pstree(process.pid, processes);
++ if (tree.isError()) {
++ return Error(tree.error());
++ }
++ children.push_back(tree.get());
++ }
++ }
++
++ foreach (const Process& process, processes) {
++ if (process.pid == pid) {
++ return ProcessTree(process, children);
++ }
++ }
++
++ return Error("No process found at " + stringify(pid));
++}
++
++
++// Returns a process tree for the specified pid (or all processes if
++// pid is none or the current process if pid is 0).
++inline Try<ProcessTree> pstree(Option<pid_t> pid = None())
++{
++ if (pid.isNone()) {
++ pid = 1;
++ } else if (pid.get() == 0) {
++ pid = getpid();
++ }
++
++ const Try<std::list<Process> >& processes = os::processes();
++
++ if (processes.isError()) {
++ return Error(processes.error());
++ }
++
++ return pstree(pid.get(), processes.get());
++}
++
++
++// Returns the minimum list of process trees that include all of the
++// specified pids using the specified list of processes.
++inline Try<std::list<ProcessTree> > pstrees(
++ const std::set<pid_t>& pids,
++ const std::list<Process>& processes)
++{
++ std::list<ProcessTree> trees;
++
++ foreach (pid_t pid, pids) {
++ // First, check if the pid is already connected to one of the
++ // process trees we've constructed.
++ bool disconnected = true;
++ foreach (const ProcessTree& tree, trees) {
++ if (tree.contains(pid)) {
++ disconnected = false;
++ break;
++ }
++ }
++
++ if (disconnected) {
++ Try<ProcessTree> tree = pstree(pid, processes);
++ if (tree.isError()) {
++ return Error(tree.error());
++ }
++
++ // Now see if any of the existing process trees are actually
++ // contained within the process tree we just created and only
++ // includ the disjoint process trees.
++ // C++11:
++ // trees = trees.filter([] (const ProcessTree& t) {
++ // return tree.get().contains(t);
++ // });
++ std::list<ProcessTree> trees_ = trees;
++ trees.clear();
++ foreach (const ProcessTree& t, trees_) {
++ if (tree.get().contains(t.process.pid)) {
++ continue;
++ }
++ trees.push_back(t);
++ }
++ trees.push_back(tree.get());
++ }
++ }
++
++ return trees;
++}
++
++} // namespace os {
++
++#endif // __STOUT_OS_PSTREE_HPP__
+diff --git a/include/mesos/stout/os/read.hpp b/include/mesos/stout/os/read.hpp
+new file mode 100644
+index 0000000..587b7b9
+--- /dev/null
++++ b/include/mesos/stout/os/read.hpp
+@@ -0,0 +1,97 @@
++#ifndef __STOUT_OS_READ_HPP__
++#define __STOUT_OS_READ_HPP__
++
++#include <stdio.h>
++#include <unistd.h>
++
++#include <stout/error.hpp>
++#include <stout/try.hpp>
++
++namespace os {
++
++// Reads 'size' bytes from a file from its current offset.
++// If EOF is encountered before reading size bytes, then the offset
++// is restored and none is returned.
++inline Result<std::string> read(int fd, size_t size)
++{
++ // Save the current offset.
++ off_t current = lseek(fd, 0, SEEK_CUR);
++ if (current == -1) {
++ return ErrnoError("Failed to lseek to SEEK_CUR");
++ }
++
++ char* buffer = new char[size];
++ size_t offset = 0;
++
++ while (offset < size) {
++ ssize_t length = ::read(fd, buffer + offset, size - offset);
++
++ if (length < 0) {
++ // TODO(bmahler): Handle a non-blocking fd? (EAGAIN, EWOULDBLOCK)
++ if (errno == EINTR) {
++ continue;
++ }
++ // Attempt to restore the original offset.
++ lseek(fd, current, SEEK_SET);
++ delete[] buffer;
++ return ErrnoError();
++ } else if (length == 0) {
++ // Reached EOF before expected! Restore the offset.
++ lseek(fd, current, SEEK_SET);
++ delete[] buffer;
++ return None();
++ }
++
++ offset += length;
++ }
++
++ std::string result = std::string(buffer, size);
++ delete[] buffer;
++ return result;
++}
++
++
++// Returns the contents of the file.
++inline Try<std::string> read(const std::string& path)
++{
++ FILE* file = fopen(path.c_str(), "r");
++ if (file == NULL) {
++ return ErrnoError("Failed to open file '" + path + "'");
++ }
++
++ // Initially the 'line' is NULL and length 0, getline() allocates
++ // ('malloc') a buffer for reading the line.
++ // In subsequent iterations, if the buffer is not large enough to
++ // hold the line, getline() resizes it with 'realloc' and updates
++ // 'line' and 'length' as necessary. See:
++ // - http://pubs.opengroup.org/onlinepubs/9699919799/functions/getline.html
++ // - http://man7.org/linux/man-pages/man3/getline.3.html
++ std::string result;
++ char* line = NULL;
++ size_t length = 0;
++ ssize_t read;
++
++ while ((read = getline(&line, &length, file)) != -1) {
++ result.append(line, read);
++ }
++
++ // getline() requires the line buffer to be freed by the caller.
++ free(line);
++
++ if (ferror(file)) {
++ ErrnoError error;
++ // NOTE: We ignore the error from fclose(). This is because
++ // users calling this function are interested in the return value
++ // of read(). Also an unsuccessful fclose() does not affect the
++ // read.
++ fclose(file);
++ return error;
++ }
++
++ fclose(file);
++ return result;
++}
++
++} // namespace os {
++
++#endif // __STOUT_OS_READ_HPP__
+diff --git a/include/mesos/stout/os/sendfile.hpp b/include/mesos/stout/os/sendfile.hpp
+new file mode 100644
+index 0000000..668e4da
+--- /dev/null
++++ b/include/mesos/stout/os/sendfile.hpp
+@@ -0,0 +1,55 @@
++#ifndef __STOUT_OS_SENDFILE_HPP__
++#define __STOUT_OS_SENDFILE_HPP__
++
++#include <errno.h>
++
++#ifdef __linux__
++#include <sys/sendfile.h>
++#endif // __linux__
++#ifdef __APPLE__
++#include <sys/socket.h>
++#include <sys/types.h>
++#include <sys/uio.h>
++#endif // __APPLE__
++
++#ifdef __linux__
++#include <stout/fatal.hpp>
++#endif // __linux__
++#include <stout/os/signals.hpp>
++
++namespace os {
++
++// Returns the amount of bytes written from the input file
++// descriptor to the output socket. On error, returns -1 and
++// errno indicates the error.
++// NOTE: The following limitations exist because of the OS X
++// implementation of sendfile:
++// 1. s must be a stream oriented socket descriptor.
++// 2. fd must be a regular file descriptor.
++inline ssize_t sendfile(int s, int fd, off_t offset, size_t length)
++{
++#ifdef __linux__
++ suppress (SIGPIPE) {
++ // This will set errno to EPIPE if a SIGPIPE occurs.
++ return ::sendfile(s, fd, &offset, length);
++ }
++ fatal("Unreachable statement");
++ return -1;
++#elif defined __APPLE__
++ // On OS X, sendfile does not need to have SIGPIPE suppressed.
++ off_t _length = static_cast<off_t>(length);
++
++ if (::sendfile(fd, s, offset, &_length, NULL, 0) < 0) {
++ if (errno == EAGAIN && _length > 0) {
++ return _length;
++ }
++ return -1;
++ }
++
++ return _length;
++#endif // __APPLE__
++}
++
++} // namespace os {
++
++#endif // __STOUT_OS_SENDFILE_HPP__
+diff --git a/include/mesos/stout/os/signals.hpp b/include/mesos/stout/os/signals.hpp
+new file mode 100644
+index 0000000..215ee55
+--- /dev/null
++++ b/include/mesos/stout/os/signals.hpp
+@@ -0,0 +1,150 @@
++#ifndef __STOUT_OS_SIGNALS_HPP__
++#define __STOUT_OS_SIGNALS_HPP__
++
++#include <errno.h>
++#include <pthread.h>
++#include <signal.h>
++#include <unistd.h>
++
++namespace os {
++
++namespace signals {
++
++// Returns true iff the signal is pending.
++inline bool pending(int signal)
++{
++ sigset_t set;
++ sigemptyset(&set);
++ sigpending(&set);
++ return sigismember(&set, signal);
++}
++
++
++// Returns true if the signal has been blocked, or false if the
++// signal was already blocked.
++inline bool block(int signal)
++{
++ sigset_t set;
++ sigaddset(&set, signal);
++
++ sigset_t oldset;
++ sigemptyset(&oldset);
++
++ // We ignore errors here as the only documented one is
++ // EINVAL due to a bad value of the SIG_* argument.
++ pthread_sigmask(SIG_BLOCK, &set, &oldset);
++
++ return !sigismember(&oldset, signal);
++}
++
++
++// Returns true if the signal has been unblocked, or false if the
++// signal was not previously blocked.
++inline bool unblock(int signal)
++{
++ sigset_t set;
++ sigaddset(&set, signal);
++
++ sigset_t oldset;
++ sigemptyset(&oldset);
++
++ pthread_sigmask(SIG_UNBLOCK, &set, &oldset);
++
++ return sigismember(&oldset, signal);
++}
++
++namespace internal {
++
++// Suppresses a signal on the current thread for the lifetime of
++// the Suppressor. The signal *must* be synchronous and delivered
++// per-thread. The suppression occurs only on the thread of
++// execution of the Suppressor.
++struct Suppressor
++{
++ Suppressor(int _signal)
++ : signal(_signal), pending(false), unblock(false)
++ {
++ // Check to see if the signal is already reported as pending.
++ // If pending, it means the thread already blocks the signal!
++ // Therefore, any new instances of the signal will also be
++ // blocked and merged with the pending one since there is no
++ // queuing for signals.
++ pending = signals::pending(signal);
++
++ if (!pending) {
++ // Block the signal for this thread only. If already blocked,
++ // there's no need to unblock it.
++ unblock = signals::block(signal);
++ }
++ }
++
++ ~Suppressor()
++ {
++ // We want to preserve errno when the Suppressor drops out of
++ // scope. Otherwise, one needs to potentially store errno when
++ // using the suppress() macro.
++ int _errno = errno;
++
++ // If the signal has become pending after we blocked it, we
++ // need to clear it before unblocking it.
++ if (!pending && signals::pending(signal)) {
++ // It is possible that in between having observed the pending
++ // signal with sigpending() and clearing it with sigwait(),
++ // the signal was delivered to another thread before we were
++ // able to clear it here. This can happen if the signal was
++ // generated for the whole process (e.g. a kill was issued).
++ // See 2.4.1 here:
++ // http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_04.html
++ // To handle the above scenario, one can either:
++ // 1. Use sigtimedwait() with a timeout of 0, to ensure we
++ // don't block forever. However, this only works on Linux
++ // and we may still swallow the signal intended for the
++ // process.
++ // 2. After seeing the pending signal, signal ourselves with
++ // pthread_kill prior to calling sigwait(). This can still
++ // swallow the signal intended for the process.
++ // We chose to use the latter technique as it works on all
++ // POSIX systems and is less likely to swallow process signals,
++ // provided the thread signal and process signal are not merged.
++ pthread_kill(pthread_self(), signal);
++
++ sigset_t mask;
++ sigemptyset(&mask);
++ sigaddset(&mask, signal);
++
++ int result;
++ do {
++ int _ignored;
++ result = sigwait(&mask, &_ignored);
++ } while (result == -1 && errno == EINTR);
++ }
++
++ // Unblock the signal (only if we were the ones to block it).
++ if (unblock) {
++ signals::unblock(signal);
++ }
++
++ // Restore errno.
++ errno = _errno;
++ }
++
++ // Needed for the suppress() macro.
++ operator bool () { return true; }
++
++private:
++ const int signal;
++ bool pending; // Whether the signal is already pending.
++ bool unblock; // Whether to unblock the signal on destruction.
++};
++
++} // namespace internal {
++
++#define suppress(signal) \
++ if (os::signals::internal::Suppressor suppressor ## signal = \
++ os::signals::internal::Suppressor(signal))
++
++} // namespace signals {
++
++} // namespace os {
++
++#endif // __STOUT_OS_SIGNALS_HPP__
+diff --git a/include/mesos/stout/os/sysctl.hpp b/include/mesos/stout/os/sysctl.hpp
+new file mode 100644
+index 0000000..065aada
+--- /dev/null
++++ b/include/mesos/stout/os/sysctl.hpp
+@@ -0,0 +1,274 @@
++#ifndef __STOUT_OS_SYSCTL_HPP__
++#define __STOUT_OS_SYSCTL_HPP__
++
++// Only provide sysctl support for OS X.
++#ifndef __APPLE__
++#error "stout/os/sysctl.hpp is only available on OS X."
++#endif
++
++#include <string>
++#include <vector>
++
++#include <sys/types.h>
++#include <sys/sysctl.h>
++
++#include <stout/error.hpp>
++#include <stout/none.hpp>
++#include <stout/option.hpp>
++#include <stout/strings.hpp>
++#include <stout/try.hpp>
++
++namespace os {
++
++// Provides an abstraction for getting system information via the
++// underlying 'sysctl' system call. You describe the sysctl
++// "Management Information Base" (MIB) name via the constructor, for
++// example, to describe "maximum number of processes allowed in the
++// system" you would do:
++//
++// os::sysctl(CTL_KERN, KERN_MAXPROC)
++//
++// To _retrieve_ the value you need to use one of the 'integer',
++// 'string', or 'table' methods to indicate the type of the value
++// being retrieved. For example:
++//
++// Try<int> maxproc = os::sysctl(CTL_KERN, KERN_MAXPROC).integer();
++//
++// Note that the 'table' method requires specifying a length. If you
++// would like the length to be looked up dynamically you can just pass
++// None. Here's an example using 'table' that builds on above:
++//
++// Try<vector<kinfo_proc> > processes =
++// os::sysctl(CTL_KERN, KERN_PROC, KERN_PROC_ALL).table(maxprox.get());
++//
++// TODO(benh): Provide an 'integer(i)', 'string(s)', and 'table(t)' to
++// enable setting system information.
++struct sysctl
++{
++ // Note that we create a constructor for each number of levels
++ // because we can't pick a suitable default for unused levels (in
++ // order to distinguish no value from some value) and while Option
++ // would solve that it could also cause people to use None which
++ // we'd need to later handle as an error.
++ explicit sysctl(int level1);
++ sysctl(int level1, int level2);
++ sysctl(int level1, int level2, int level3);
++ sysctl(int level1, int level2, int level3, int level4);
++ sysctl(int level1, int level2, int level3, int level4, int level5);
++ ~sysctl();
++
++ // Get system information as an integer.
++private: struct Integer; // Forward declaration.
++public:
++ Integer integer() const;
++
++ // Get system information as a string.
++ Try<std::string> string() const;
++
++ // Get system information as a table, optionally specifying a
++ // length. Note that this function is lazy and will not actually
++ // perform the syscall until you cast (implicitely or explicitly) a
++ // 'Table' to a std::vector<T>. For example, to get the first 10
++ // processes in the process table you can do:
++ //
++ // Try<std::vector<kinfo_proc> > processes =
++ // os::sysctl(CTL_KERN, KERN_PROC, KERN_PROC_ALL).table(10);
++ //
++private: struct Table; // Forward declaration.
++public:
++ Table table(const Option<size_t>& length = None()) const;
++
++private:
++ struct Integer
++ {
++ Integer(int _levels, int* _name);
++
++ template <typename T>
++ operator Try<T> ();
++
++ const int levels;
++ int* name;
++ };
++
++ struct Table
++ {
++ Table(int _levels, int* _name, const Option<size_t>& _length);
++
++ template <typename T>
++ operator Try<std::vector<T> > ();
++
++ const int levels;
++ int* name;
++ Option<size_t> length;
++ };
++
++ const int levels;
++ int* name;
++};
++
++
++inline sysctl::sysctl(int level1)
++ : levels(1), name(new int[levels])
++{
++ name[0] = level1;
++}
++
++
++inline sysctl::sysctl(int level1, int level2)
++ : levels(2), name(new int[levels])
++{
++ name[0] = level1;
++ name[1] = level2;
++}
++
++
++inline sysctl::sysctl(int level1, int level2, int level3)
++ : levels(3), name(new int[levels])
++{
++ name[0] = level1;
++ name[1] = level2;
++ name[2] = level3;
++}
++
++
++inline sysctl::sysctl(int level1, int level2, int level3, int level4)
++ : levels(4), name(new int[levels])
++{
++ name[0] = level1;
++ name[1] = level2;
++ name[2] = level3;
++ name[3] = level4;
++}
++
++
++inline sysctl::sysctl(int level1, int level2, int level3, int level4, int level5)
++ : levels(5), name(new int[levels])
++{
++ name[0] = level1;
++ name[1] = level2;
++ name[2] = level3;
++ name[3] = level4;
++ name[4] = level5;
++}
++
++
++inline sysctl::~sysctl()
++{
++ delete[] name;
++}
++
++
++inline sysctl::Integer sysctl::integer() const
++{
++ return Integer(levels, name);
++}
++
++
++inline Try<std::string> sysctl::string() const
++{
++ // First determine the size of the string.
++ size_t size = 0;
++ if (::sysctl(name, levels, NULL, &size, NULL, 0) == -1) {
++ return ErrnoError();
++ }
++
++ // Now read it.
++ size_t length = size / sizeof(char);
++ char* temp = new char[length];
++ if (::sysctl(name, levels, temp, &size, NULL, 0) == -1) {
++ Error error = ErrnoError();
++ delete[] temp;
++ return error;
++ }
++
++ // TODO(benh): It's possible that the value has changed since we
++ // determined it's length above. We should really check that we
++ // get back the same length and if not throw an error.
++
++ // The "string" in 'temp' might include null bytes, so to get all of
++ // the data we need to create a string with 'size' (but we exclude
++ // the last null byte via 'size - 1').
++ std::string result(temp, size - 1);
++ delete[] temp;
++ return result;
++}
++
++
++inline sysctl::Table sysctl::table(const Option<size_t>& length) const
++{
++ return Table(levels, name, length);
++}
++
++
++inline sysctl::Integer::Integer(
++ int _levels,
++ int* _name)
++ : levels(_levels),
++ name(_name)
++{}
++
++
++template <typename T>
++sysctl::Integer::operator Try<T> ()
++{
++ T i;
++ size_t size = sizeof(i);
++ if (::sysctl(name, levels, &i, &size, NULL, 0) == -1) {
++ return ErrnoError();
++ }
++ return i;
++}
++
++
++inline sysctl::Table::Table(
++ int _levels,
++ int* _name,
++ const Option<size_t>& _length)
++ : levels(_levels),
++ name(_name),
++ length(_length)
++{}
++
++
++template <typename T>
++sysctl::Table::operator Try<std::vector<T> > ()
++{
++ size_t size = 0;
++ if (length.isNone()) {
++ if (::sysctl(name, levels, NULL, &size, NULL, 0) == -1) {
++ return ErrnoError();
++ }
++ if (size % sizeof(T) != 0) {
++ return Error("Failed to determine the length of result, "
++ "amount of available data is not a multiple "
++ "of the table type");
++ }
++ length = Option<size_t>(size / sizeof(T));
++ }
++
++ T* ts = new T[length.get()];
++ size = length.get() * sizeof(T);
++ if (::sysctl(name, levels, ts, &size, NULL, 0) == -1) {
++ Error error = ErrnoError();
++ delete[] ts;
++ return error;
++ }
++
++ // TODO(benh): It's possible that the value has changed since we
++ // determined it's length above (or from what was specified). We
++ // should really check that we get back the same length and if not
++ // throw an error.
++
++ length = size / sizeof(T);
++
++ std::vector<T> results;
++ for (size_t i = 0; i < length.get(); i++) {
++ results.push_back(ts[i]);
++ }
++ delete[] ts;
++ return results;
++}
++
++} // namespace os {
++
++#endif // __STOUT_OS_SYSCTL_HPP__
+diff --git a/include/mesos/stout/path.hpp b/include/mesos/stout/path.hpp
+new file mode 100644
+index 0000000..fda4e04
+--- /dev/null
++++ b/include/mesos/stout/path.hpp
+@@ -0,0 +1,76 @@
++#ifndef __STOUT_PATH_HPP__
++#define __STOUT_PATH_HPP__
++
++#include <string>
++#include <vector>
++
++#include "strings.hpp"
++
++namespace path {
++
++inline std::string join(const std::string& path1, const std::string& path2)
++{
++ return
++ strings::remove(path1, "/", strings::SUFFIX) + "/" +
++ strings::remove(path2, "/", strings::PREFIX);
++}
++
++
++inline std::string join(
++ const std::string& path1,
++ const std::string& path2,
++ const std::string& path3)
++{
++ return join(path1, join(path2, path3));
++}
++
++
++inline std::string join(
++ const std::string& path1,
++ const std::string& path2,
++ const std::string& path3,
++ const std::string& path4)
++{
++ return join(path1, join(path2, path3, path4));
++}
++
++
++inline std::string join(
++ const std::string& path1,
++ const std::string& path2,
++ const std::string& path3,
++ const std::string& path4,
++ const std::string& path5)
++{
++ return join(path1, join(path2, join(path3, join(path4, path5))));
++}
++
++
++inline std::string join(
++ const std::string& path1,
++ const std::string& path2,
++ const std::string& path3,
++ const std::string& path4,
++ const std::string& path5,
++ const std::string& path6)
++{
++ return join(path1, join(path2, path3, path4, path5, path6));
++}
++
++
++inline std::string join(const std::vector<std::string>& paths)
++{
++ if (paths.empty()) {
++ return "";
++ }
++
++ std::string result = paths[0];
++ for (size_t i = 1; i < paths.size(); ++i) {
++ result = join(result, paths[i]);
++ }
++ return result;
++}
++
++} // namespace path {
++
++#endif // __STOUT_PATH_HPP__
+diff --git a/include/mesos/stout/preprocessor.hpp b/include/mesos/stout/preprocessor.hpp
+new file mode 100644
+index 0000000..bd9c411
+--- /dev/null
++++ b/include/mesos/stout/preprocessor.hpp
+@@ -0,0 +1,29 @@
++#ifndef __STOUT_PROCESS_PREPROCESSOR_HPP__
++#define __STOUT_PROCESS_PREPROCESSOR_HPP__
++
++#include <boost/preprocessor/cat.hpp>
++
++#include <boost/preprocessor/arithmetic/inc.hpp>
++
++#include <boost/preprocessor/facilities/intercept.hpp>
++
++#include <boost/preprocessor/repetition/enum_params.hpp>
++#include <boost/preprocessor/repetition/enum_binary_params.hpp>
++#include <boost/preprocessor/repetition/enum_trailing_params.hpp>
++#include <boost/preprocessor/repetition/repeat.hpp>
++#include <boost/preprocessor/repetition/repeat_from_to.hpp>
++
++// Provides aliases to a bunch of preprocessor macros useful for
++// creating template definitions that have varying number of
++// parameters (should be removable with C++-11 variadic templates).
++
++#define CAT BOOST_PP_CAT
++#define INC BOOST_PP_INC
++#define INTERCEPT BOOST_PP_INTERCEPT
++#define ENUM_PARAMS BOOST_PP_ENUM_PARAMS
++#define ENUM_BINARY_PARAMS BOOST_PP_ENUM_BINARY_PARAMS
++#define ENUM_TRAILING_PARAMS BOOST_PP_ENUM_TRAILING_PARAMS
++#define REPEAT BOOST_PP_REPEAT
++#define REPEAT_FROM_TO BOOST_PP_REPEAT_FROM_TO
++
++#endif // __STOUT_PROCESS_PREPROCESSOR_HPP__
+diff --git a/include/mesos/stout/proc.hpp b/include/mesos/stout/proc.hpp
+new file mode 100644
+index 0000000..1bbbaa1
+--- /dev/null
++++ b/include/mesos/stout/proc.hpp
+@@ -0,0 +1,493 @@
++/**
++ * Licensed to the Apache Software Foundation (ASF) under one
++ * or more contributor license agreements. See the NOTICE file
++ * distributed with this work for additional information
++ * regarding copyright ownership. The ASF licenses this file
++ * to you under the Apache License, Version 2.0 (the
++ * "License"); you may not use this file except in compliance
++ * with the License. You may obtain a copy of the License at
++ *
++ * http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++
++#ifndef __STOUT_PROC_HPP__
++#define __STOUT_PROC_HPP__
++
++// This file contains linux-only utilities for /proc.
++#ifndef __linux__
++#error "stout/proc.hpp is only available on Linux systems."
++#endif
++
++#include <errno.h>
++#include <signal.h>
++
++#include <sys/types.h> // For pid_t.
++
++#include <fstream>
++#include <list>
++#include <queue>
++#include <set>
++#include <sstream> // For 'std::istringstream'.
++#include <string>
++#include <vector>
++
++#include <stout/error.hpp>
++#include <stout/foreach.hpp>
++#include <stout/none.hpp>
++#include <stout/numify.hpp>
++#include <stout/option.hpp>
++#include <stout/strings.hpp>
++#include <stout/try.hpp>
++
++#include <stout/os/exists.hpp>
++#include <stout/os/ls.hpp>
++#include <stout/os/read.hpp>
++
++namespace proc {
++
++// Snapshot of a process (modeled after /proc/[pid]/stat).
++// For more information, see:
++// http://www.kernel.org/doc/Documentation/filesystems/proc.txt
++struct ProcessStatus
++{
++ ProcessStatus(
++ pid_t _pid,
++ const std::string& _comm,
++ char _state,
++ pid_t _ppid,
++ pid_t _pgrp,
++ pid_t _session,
++ int _tty_nr,
++ pid_t _tpgid,
++ unsigned int _flags,
++ unsigned long _minflt,
++ unsigned long _cminflt,
++ unsigned long _majflt,
++ unsigned long _cmajflt,
++ unsigned long _utime,
++ unsigned long _stime,
++ long _cutime,
++ long _cstime,
++ long _priority,
++ long _nice,
++ long _num_threads,
++ long _itrealvalue,
++ unsigned long long _starttime,
++ unsigned long _vsize,
++ long _rss,
++ unsigned long _rsslim,
++ unsigned long _startcode,
++ unsigned long _endcode,
++ unsigned long _startstack,
++ unsigned long _kstkeip,
++ unsigned long _signal,
++ unsigned long _blocked,
++ unsigned long _sigcatch,
++ unsigned long _wchan,
++ unsigned long _nswap,
++ unsigned long _cnswap)
++ : pid(_pid),
++ comm(_comm),
++ state(_state),
++ ppid(_ppid),
++ pgrp(_pgrp),
++ session(_session),
++ tty_nr(_tty_nr),
++ tpgid(_tpgid),
++ flags(_flags),
++ minflt(_minflt),
++ cminflt(_cminflt),
++ majflt(_majflt),
++ cmajflt(_cmajflt),
++ utime(_utime),
++ stime(_stime),
++ cutime(_cutime),
++ cstime(_cstime),
++ priority(_priority),
++ nice(_nice),
++ num_threads(_num_threads),
++ itrealvalue(_itrealvalue),
++ starttime(_starttime),
++ vsize(_vsize),
++ rss(_rss),
++ rsslim(_rsslim),
++ startcode(_startcode),
++ endcode(_endcode),
++ startstack(_startstack),
++ kstkeip(_kstkeip),
++ signal(_signal),
++ blocked(_blocked),
++ sigcatch(_sigcatch),
++ wchan(_wchan),
++ nswap(_nswap),
++ cnswap(_cnswap) {}
++
++ const pid_t pid;
++ const std::string comm;
++ const char state;
++ const pid_t ppid;
++ const pid_t pgrp;
++ const pid_t session;
++ const int tty_nr;
++ const pid_t tpgid;
++ const unsigned int flags;
++ const unsigned long minflt;
++ const unsigned long cminflt;
++ const unsigned long majflt;
++ const unsigned long cmajflt;
++ const unsigned long utime;
++ const unsigned long stime;
++ const long cutime;
++ const long cstime;
++ const long priority;
++ const long nice;
++ const long num_threads;
++ const long itrealvalue;
++ const unsigned long long starttime;
++ const unsigned long vsize;
++ const long rss;
++ const unsigned long rsslim;
++ const unsigned long startcode;
++ const unsigned long endcode;
++ const unsigned long startstack;
++ const unsigned long kstkeip;
++ const unsigned long signal;
++ const unsigned long blocked;
++ const unsigned long sigcatch;
++ const unsigned long wchan;
++ const unsigned long nswap;
++ const unsigned long cnswap;
++};
++
++
++// Returns the process statistics from /proc/[pid]/stat.
++// The return value is None if the process does not exist.
++inline Result<ProcessStatus> status(pid_t pid)
++{
++ std::string path = "/proc/" + stringify(pid) + "/stat";
++
++ Try<std::string> read = os::read(path);
++ if (read.isError()) {
++ // Need to check if file exists AFTER we open it to guarantee
++ // process hasn't terminated.
++ if (!os::exists(path)) {
++ return None();
++ }
++ return Error(read.error());
++ }
++
++ std::istringstream data(read.get());
++
++ std::string comm;
++ char state;
++ pid_t ppid;
++ pid_t pgrp;
++ pid_t session;
++ int tty_nr;
++ pid_t tpgid;
++ unsigned int flags;
++ unsigned long minflt;
++ unsigned long cminflt;
++ unsigned long majflt;
++ unsigned long cmajflt;
++ unsigned long utime;
++ unsigned long stime;
++ long cutime;
++ long cstime;
++ long priority;
++ long nice;
++ long num_threads;
++ long itrealvalue;
++ unsigned long long starttime;
++ unsigned long vsize;
++ long rss;
++ unsigned long rsslim;
++ unsigned long startcode;
++ unsigned long endcode;
++ unsigned long startstack;
++ unsigned long kstkeip;
++ unsigned long signal;
++ unsigned long blocked;
++ unsigned long sigcatch;
++ unsigned long wchan;
++ unsigned long nswap;
++ unsigned long cnswap;
++
++ // NOTE: The following are unused for now.
++ // int exit_signal;
++ // int processor;
++ // unsigned int rt_priority;
++ // unsigned int policy;
++ // unsigned long long delayacct_blkio_ticks;
++ // unsigned long guest_time;
++ // unsigned int cguest_time;
++
++ std::string _; // For ignoring fields.
++
++ // Parse all fields from stat.
++ data >> _ >> comm >> state >> ppid >> pgrp >> session >> tty_nr
++ >> tpgid >> flags >> minflt >> cminflt >> majflt >> cmajflt
++ >> utime >> stime >> cutime >> cstime >> priority >> nice
++ >> num_threads >> itrealvalue >> starttime >> vsize >> rss
++ >> rsslim >> startcode >> endcode >> startstack >> kstkeip
++ >> signal >> blocked >> sigcatch >> wchan >> nswap >> cnswap;
++
++ // Check for any read/parse errors.
++ if (data.fail() && !data.eof()) {
++ return Error("Failed to read/parse '" + path + "'");
++ }
++
++ // Remove the parentheses that is wrapped around 'comm' (when
++ // printing out the process in a process tree we use parentheses to
++ // indicate "zombie" processes).
++ comm = strings::remove(comm, "(", strings::PREFIX);
++ comm = strings::remove(comm, ")", strings::SUFFIX);
++
++ return ProcessStatus(pid, comm, state, ppid, pgrp, session, tty_nr,
++ tpgid, flags, minflt, cminflt, majflt, cmajflt,
++ utime, stime, cutime, cstime, priority, nice,
++ num_threads, itrealvalue, starttime, vsize, rss,
++ rsslim, startcode, endcode, startstack, kstkeip,
++ signal, blocked, sigcatch, wchan, nswap, cnswap);
++}
++
++
++inline Result<std::string> cmdline(const Option<pid_t>& pid = None())
++{
++ const std::string path = pid.isSome()
++ ? "/proc/" + stringify(pid.get()) + "/cmdline"
++ : "/proc/cmdline";
++
++ std::ifstream file(path.c_str());
++
++ if (!file.is_open()) {
++ // Need to check if file exists AFTER we open it to guarantee
++ // process hasn't terminated (or if it has, we at least have a
++ // file which the kernel _should_ respect until a close).
++ if (!os::exists(path)) {
++ return None();
++ }
++ return Error("Failed to open '" + path + "'");
++ }
++
++ std::stringbuf buffer;
++
++ do {
++ // Read each argument in "argv", separated by null bytes.
++ file.get(buffer, '\0');
++
++ // Check for any read errors.
++ if (file.fail() && !file.eof()) {
++ file.close();
++ return Error("Failed to read '" + path + "'");
++ } else if (!file.eof()) {
++ file.get(); // Read the null byte.
++ buffer.sputc(' '); // Put a space between each command line argument.
++ }
++ } while (!file.eof());
++
++ return buffer.str();
++}
++
++
++// Reads from /proc and returns a list of all running processes.
++inline Try<std::set<pid_t> > pids()
++{
++ std::set<pid_t> pids;
++
++ foreach (const std::string& file, os::ls("/proc")) {
++ Try<pid_t> pid = numify<pid_t>(file);
++ if (pid.isSome()) {
++ pids.insert(pid.get()); // Ignore files that can't be numified.
++ }
++ }
++
++ if (!pids.empty()) {
++ return pids;
++ }
++
++ return Error("Failed to determine pids from /proc");
++}
++
++
++// Snapshot of a system (modeled after /proc/stat).
++struct SystemStatus
++{
++ SystemStatus(unsigned long long _btime) : btime(_btime) {}
++
++ const unsigned long long btime; // Boot time.
++ // TODO(benh): Add more.
++};
++
++
++// Returns the system statistics from /proc/stat.
++inline Try<SystemStatus> status()
++{
++ unsigned long long btime = 0;
++
++ std::ifstream file("/proc/stat");
++
++ if (!file.is_open()) {
++ return Error("Failed to open /proc/stat");
++ }
++
++ std::string line;
++ while (std::getline(file, line)) {
++ if (line.find("btime ") == 0) {
++ Try<unsigned long long> number =
++ numify<unsigned long long>(line.substr(6));
++
++ if (number.isError()) {
++ return Error("Failed to parse /proc/stat: " + number.error());
++ }
++
++ btime = number.get();
++ break;
++ }
++ }
++
++ if (file.fail() && !file.eof()) {
++ file.close();
++ return Error("Failed to read /proc/stat");
++ }
++
++ file.close();
++
++ return SystemStatus(btime);
++}
++
++
++// Representation of a processor (really an execution unit since this
++// captures "hardware threads" as well) modeled after /proc/cpuinfo.
++struct CPU
++{
++ CPU(unsigned int _id, unsigned int _core, unsigned int _socket)
++ : id(_id), core(_core), socket(_socket) {}
++
++ // These are non-const because we need the default assignment operator.
++ unsigned int id; // "processor"
++ unsigned int core; // "core id"
++ unsigned int socket; // "physical id"
++};
++
++
++inline bool operator == (const CPU& lhs, const CPU& rhs)
++{
++ return (lhs.id == rhs.id) && (lhs.core == rhs.core) &&
++ (lhs.socket == rhs.socket);
++}
++
++
++inline bool operator < (const CPU& lhs, const CPU& rhs)
++{
++ // Sort by (socket, core, id).
++ if (lhs.socket != rhs.socket) {
++ return lhs.socket < rhs.socket;
++ }
++
++ // On the same socket.
++ if (lhs.core != rhs.core) {
++ return lhs.core < rhs.core;
++ }
++
++ // On the same core.
++ return lhs.id < rhs.id;
++}
++
++
++inline std::ostream& operator << (std::ostream& out, const CPU& cpu)
++{
++ return out << "CPU (id:" << cpu.id << ", "
++ << "core:" << cpu.core << ", "
++ << "socket:" << cpu.socket << ")";
++}
++
++
++// Reads from /proc/cpuinfo and returns a list of CPUs.
++inline Try<std::list<CPU> > cpus()
++{
++ std::list<CPU> results;
++
++ std::ifstream file("/proc/cpuinfo");
++
++ if (!file.is_open()) {
++ return Error("Failed to open /proc/cpuinfo");
++ }
++
++ // Placeholders as we parse the file.
++ Option<unsigned int> id;
++ Option<unsigned int> core;
++ Option<unsigned int> socket;
++
++ std::string line;
++ while (std::getline(file, line)) {
++ if (line.find("processor") == 0 ||
++ line.find("physical id") == 0 ||
++ line.find("core id") == 0) {
++ // Get out and parse the value.
++ std::vector<std::string> tokens = strings::tokenize(line, ": ");
++
++ if (tokens.size() < 2) {
++ return Error("Unexpected format in /proc/cpuinfo: " +
++ stringify(tokens));
++ }
++
++ Try<unsigned int> value = numify<unsigned int>(tokens.back());
++ if (value.isError()) {
++ return Error(value.error());
++ }
++
++ // Now save the value.
++ if (line.find("processor") == 0) {
++ if (id.isSome()) {
++ // The physical id and core id are not present in this case.
++ results.push_back(CPU(id.get(), 0, 0));
++ }
++ id = value.get();
++ } else if (line.find("physical id") == 0) {
++ if (socket.isSome()) {
++ return Error("Unexpected format in /proc/cpuinfo");
++ }
++ socket = value.get();
++ } else if (line.find("core id") == 0) {
++ if (core.isSome()) {
++ return Error("Unexpected format in /proc/cpuinfo");
++ }
++ core = value.get();
++ }
++
++ // And finally create a CPU if we have all the information.
++ if (id.isSome() && core.isSome() && socket.isSome()) {
++ results.push_back(CPU(id.get(), core.get(), socket.get()));
++ id = None();
++ core = None();
++ socket = None();
++ }
++ }
++ }
++
++ // Add the last processor if the physical id and core id were not present.
++ if (id.isSome()) {
++ // The physical id and core id are not present.
++ results.push_back(CPU(id.get(), 0, 0));
++ }
++
++ if (file.fail() && !file.eof()) {
++ file.close();
++ return Error("Failed to read /proc/cpuinfo");
++ }
++
++ file.close();
++
++ return results;
++}
++
++} // namespace proc {
++
++#endif // __STOUT_PROC_HPP__
+diff --git a/include/mesos/stout/protobuf.hpp b/include/mesos/stout/protobuf.hpp
+new file mode 100644
+index 0000000..3fa7fe6
+--- /dev/null
++++ b/include/mesos/stout/protobuf.hpp
+@@ -0,0 +1,312 @@
++#ifndef __STOUT_PROTOBUF_HPP__
++#define __STOUT_PROTOBUF_HPP__
++
++#include <assert.h>
++#include <errno.h>
++#include <stdint.h>
++#include <unistd.h>
++
++#include <sys/types.h>
++
++#include <glog/logging.h>
++
++#include <google/protobuf/descriptor.h>
++#include <google/protobuf/message.h>
++
++#include <google/protobuf/io/zero_copy_stream_impl.h>
++
++#include <string>
++
++#include <boost/lexical_cast.hpp>
++
++#include "error.hpp"
++#include "json.hpp"
++#include "none.hpp"
++#include "os.hpp"
++#include "result.hpp"
++#include "try.hpp"
++
++namespace protobuf {
++
++// Write out the given protobuf to the specified file descriptor by
++// first writing out the length of the protobuf followed by the contents.
++// NOTE: On error, this may have written partial data to the file.
++inline Try<Nothing> write(int fd, const google::protobuf::Message& message)
++{
++ if (!message.IsInitialized()) {
++ return Error("Uninitialized protocol buffer");
++ }
++
++ // First write the size of the protobuf.
++ uint32_t size = message.ByteSize();
++ std::string bytes = std::string((char*) &size, sizeof(size));
++
++ Try<Nothing> result = os::write(fd, bytes);
++ if (result.isError()) {
++ return Error("Failed to write size: " + result.error());
++ }
++
++ if (!message.SerializeToFileDescriptor(fd)) {
++ return Error("Failed to write/serialize message");
++ }
++
++ return Nothing();
++}
++
++
++// A wrapper function that wraps the above write with open and closing the file.
++inline Try<Nothing> write(
++ const std::string& path,
++ const google::protobuf::Message& message)
++{
++ Try<int> fd = os::open(
++ path,
++ O_WRONLY | O_CREAT | O_TRUNC,
++ S_IRUSR | S_IWUSR | S_IRGRP | S_IRWXO);
++
++ if (fd.isError()) {
++ return Error("Failed to open file '" + path + "': " + fd.error());
++ }
++
++ Try<Nothing> result = write(fd.get(), message);
++
++ // NOTE: We ignore the return value of close(). This is because users calling
++ // this function are interested in the return value of write(). Also an
++ // unsuccessful close() doesn't affect the write.
++ os::close(fd.get());
++
++ return result;
++}
++
++
++// Read the next protobuf of type T from the file by first reading
++// the "size" followed by the contents (as written by 'write' above).
++// If 'ignorePartial' is true, None() is returned when we unexpectedly
++// hit EOF while reading the protobuf (e.g., partial write).
++template <typename T>
++inline Result<T> read(int fd, bool ignorePartial = false)
++{
++ // Save the offset so we can re-adjust if something goes wrong.
++ off_t offset = lseek(fd, 0, SEEK_CUR);
++ if (offset == -1) {
++ return ErrnoError("Failed to lseek to SEEK_CUR");
++ }
++
++ uint32_t size;
++ Result<std::string> result = os::read(fd, sizeof(size));
++
++ if (result.isNone()) {
++ return None(); // No more protobufs to read.
++ } else if (result.isError()) {
++ return Error("Failed to read size: " + result.error());
++ }
++
++ // Parse the size from the bytes.
++ memcpy((void*) &size, (void*) result.get().data(), sizeof(size));
++
++ // NOTE: Instead of specifically checking for corruption in 'size', we simply
++ // try to read 'size' bytes. If we hit EOF early, it is an indication of
++ // corruption.
++ result = os::read(fd, size);
++
++ if (result.isNone()) {
++ // Hit EOF unexpectedly. Restore the offset to before the size read.
++ lseek(fd, offset, SEEK_SET);
++ if (ignorePartial) {
++ return None();
++ }
++ return Error("Failed to read message of size " + stringify(size) +
++ " bytes: hit EOF unexpectedly, possible corruption");
++ } else if (result.isError()) {
++ // Restore the offset to before the size read.
++ lseek(fd, offset, SEEK_SET);
++ return Error("Failed to read message: " + result.error());
++ }
++
++ // Parse the protobuf from the string.
++ T message;
++ google::protobuf::io::ArrayInputStream stream(
++ result.get().data(), result.get().size());
++
++ if (!message.ParseFromZeroCopyStream(&stream)) {
++ // Restore the offset to before the size read.
++ lseek(fd, offset, SEEK_SET);
++ return Error("Failed to deserialize message");
++ }
++
++ return message;
++}
++
++
++// A wrapper function that wraps the above read() with
++// open and closing the file.
++template <typename T>
++inline Result<T> read(const std::string& path)
++{
++ Try<int> fd = os::open(
++ path, O_RDONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IRWXO);
++
++ if (fd.isError()) {
++ return Error("Failed to open file '" + path + "': " + fd.error());
++ }
++
++ Result<T> result = read<T>(fd.get());
++
++ // NOTE: We ignore the return value of close(). This is because users calling
++ // this function are interested in the return value of read(). Also an
++ // unsuccessful close() doesn't affect the read.
++ os::close(fd.get());
++
++ return result;
++}
++
++} // namespace protobuf {
++
++namespace JSON {
++
++struct Protobuf
++{
++ // TODO(bmahler): This currently uses the default value for optional
++ // fields but we may want to revisit this decision.
++ Protobuf(const google::protobuf::Message& message)
++ {
++ const google::protobuf::Reflection* reflection = message.GetReflection();
++ std::vector<const google::protobuf::FieldDescriptor*> fields;
++ reflection->ListFields(message, &fields);
++
++ foreach (const google::protobuf::FieldDescriptor* field, fields) {
++ if (field->is_repeated()) {
++ JSON::Array array;
++ for (int i = 0; i < reflection->FieldSize(message, field); ++i) {
++ switch (field->type()) {
++ case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
++ array.values.push_back(JSON::Number(
++ reflection->GetRepeatedDouble(message, field, i)));
++ break;
++ case google::protobuf::FieldDescriptor::TYPE_FLOAT:
++ array.values.push_back(JSON::Number(
++ reflection->GetRepeatedFloat(message, field, i)));
++ break;
++ case google::protobuf::FieldDescriptor::TYPE_INT64:
++ case google::protobuf::FieldDescriptor::TYPE_SINT64:
++ case google::protobuf::FieldDescriptor::TYPE_SFIXED64:
++ array.values.push_back(JSON::Number(
++ reflection->GetRepeatedInt64(message, field, i)));
++ break;
++ case google::protobuf::FieldDescriptor::TYPE_UINT64:
++ case google::protobuf::FieldDescriptor::TYPE_FIXED64:
++ array.values.push_back(JSON::Number(
++ reflection->GetRepeatedUInt64(message, field, i)));
++ break;
++ case google::protobuf::FieldDescriptor::TYPE_INT32:
++ case google::protobuf::FieldDescriptor::TYPE_SINT32:
++ case google::protobuf::FieldDescriptor::TYPE_SFIXED32:
++ array.values.push_back(JSON::Number(
++ reflection->GetRepeatedInt32(message, field, i)));
++ break;
++ case google::protobuf::FieldDescriptor::TYPE_UINT32:
++ case google::protobuf::FieldDescriptor::TYPE_FIXED32:
++ array.values.push_back(JSON::Number(
++ reflection->GetRepeatedUInt32(message, field, i)));
++ break;
++ case google::protobuf::FieldDescriptor::TYPE_BOOL:
++ if (reflection->GetRepeatedBool(message, field, i)) {
++ array.values.push_back(JSON::True());
++ } else {
++ array.values.push_back(JSON::False());
++ }
++ break;
++ case google::protobuf::FieldDescriptor::TYPE_STRING:
++ case google::protobuf::FieldDescriptor::TYPE_BYTES:
++ array.values.push_back(JSON::String(
++ reflection->GetRepeatedString(message, field, i)));
++ break;
++ case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
++ array.values.push_back(Protobuf(
++ reflection->GetRepeatedMessage(message, field, i)));
++ break;
++ case google::protobuf::FieldDescriptor::TYPE_ENUM:
++ array.values.push_back(JSON::String(
++ reflection->GetRepeatedEnum(message, field, i)->name()));
++ break;
++ case google::protobuf::FieldDescriptor::TYPE_GROUP:
++ // Deprecated!
++ default:
++ std::cerr << "Unhandled protobuf field type: " << field->type()
++ << std::endl;
++ abort();
++ }
++ }
++ object.values[field->name()] = array;
++ } else {
++ switch (field->type()) {
++ case google::protobuf::FieldDescriptor::TYPE_DOUBLE:
++ object.values[field->name()] =
++ JSON::Number(reflection->GetDouble(message, field));
++ break;
++ case google::protobuf::FieldDescriptor::TYPE_FLOAT:
++ object.values[field->name()] =
++ JSON::Number(reflection->GetFloat(message, field));
++ break;
++ case google::protobuf::FieldDescriptor::TYPE_INT64:
++ case google::protobuf::FieldDescriptor::TYPE_SINT64:
++ case google::protobuf::FieldDescriptor::TYPE_SFIXED64:
++ object.values[field->name()] =
++ JSON::Number(reflection->GetInt64(message, field));
++ break;
++ case google::protobuf::FieldDescriptor::TYPE_UINT64:
++ case google::protobuf::FieldDescriptor::TYPE_FIXED64:
++ object.values[field->name()] =
++ JSON::Number(reflection->GetUInt64(message, field));
++ break;
++ case google::protobuf::FieldDescriptor::TYPE_INT32:
++ case google::protobuf::FieldDescriptor::TYPE_SINT32:
++ case google::protobuf::FieldDescriptor::TYPE_SFIXED32:
++ object.values[field->name()] =
++ JSON::Number(reflection->GetInt32(message, field));
++ break;
++ case google::protobuf::FieldDescriptor::TYPE_UINT32:
++ case google::protobuf::FieldDescriptor::TYPE_FIXED32:
++ object.values[field->name()] =
++ JSON::Number(reflection->GetUInt32(message, field));
++ break;
++ case google::protobuf::FieldDescriptor::TYPE_BOOL:
++ if (reflection->GetBool(message, field)) {
++ object.values[field->name()] = JSON::True();
++ } else {
++ object.values[field->name()] = JSON::False();
++ }
++ break;
++ case google::protobuf::FieldDescriptor::TYPE_STRING:
++ case google::protobuf::FieldDescriptor::TYPE_BYTES:
++ object.values[field->name()] =
++ JSON::String(reflection->GetString(message, field));
++ break;
++ case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
++ object.values[field->name()] =
++ Protobuf(reflection->GetMessage(message, field));
++ break;
++ case google::protobuf::FieldDescriptor::TYPE_ENUM:
++ object.values[field->name()] =
++ JSON::String(reflection->GetEnum(message, field)->name());
++ break;
++ case google::protobuf::FieldDescriptor::TYPE_GROUP:
++ // Deprecated!
++ default:
++ std::cerr << "Unhandled protobuf field type: " << field->type()
++ << std::endl;
++ abort();
++ }
++ }
++ }
++ }
++
++ operator Object () const { return object; }
++
++private:
++ JSON::Object object;
++};
++
++} // namespace JSON {
++
++#endif // __STOUT_PROTOBUF_HPP__
+diff --git a/include/mesos/stout/result.hpp b/include/mesos/stout/result.hpp
+new file mode 100644
+index 0000000..f918f86
+--- /dev/null
++++ b/include/mesos/stout/result.hpp
+@@ -0,0 +1,99 @@
++#ifndef __STOUT_RESULT_HPP__
++#define __STOUT_RESULT_HPP__
++
++#include <assert.h>
++#include <stdlib.h> // For abort.
++
++#include <iostream>
++#include <string>
++
++
++template <typename T>
++class Result
++{
++public:
++ static Result<T> none()
++ {
++ return Result<T>(NONE);
++ }
++
++ static Result<T> some(const T& t)
++ {
++ return Result<T>(SOME, new T(t));
++ }
++
++ static Result<T> error(const std::string& message)
++ {
++ return Result<T>(ERROR, NULL, message);
++ }
++
++ Result(const T& _t) : state(SOME), t(new T(_t)) {}
++
++ Result(const Result<T>& that)
++ {
++ state = that.state;
++ if (that.t != NULL) {
++ t = new T(*that.t);
++ } else {
++ t = NULL;
++ }
++ message = that.message;
++ }
++
++ ~Result()
++ {
++ delete t;
++ }
++
++ Result<T>& operator = (const Result<T>& that)
++ {
++ if (this != &that) {
++ delete t;
++ state = that.state;
++ if (that.t != NULL) {
++ t = new T(*that.t);
++ } else {
++ t = NULL;
++ }
++ message = that.message;
++ }
++
++ return *this;
++ }
++
++ bool isSome() const { return state == SOME; }
++ bool isNone() const { return state == NONE; }
++ bool isError() const { return state == ERROR; }
++
++ T get() const
++ {
++ if (state != SOME) {
++ if (state == ERROR) {
++ std::cerr << "Result::get() but state == ERROR: "
++ << error() << std::endl;
++ } else if (state == NONE) {
++ std::cerr << "Result::get() but state == NONE" << std::endl;
++ }
++ abort();
++ }
++ return *t;
++ }
++
++ std::string error() const { assert(state == ERROR); return message; }
++
++private:
++ enum State {
++ SOME,
++ NONE,
++ ERROR
++ };
++
++ Result(State _state, T* _t = NULL, const std::string& _message = "")
++ : state(_state), t(_t), message(_message) {}
++
++ State state;
++ T* t;
++ std::string message;
++};
++
++#endif // __STOUT_RESULT_HPP__
+diff --git a/include/mesos/stout/set.hpp b/include/mesos/stout/set.hpp
+new file mode 100644
+index 0000000..ba7ffe8
+--- /dev/null
++++ b/include/mesos/stout/set.hpp
+@@ -0,0 +1,70 @@
++#include <algorithm> // For std::set_intersection.
++#include <set>
++#include <vector>
++
++template <typename T>
++class Set : public std::set<T>
++{
++public:
++ Set() {}
++
++ Set(const T& t1)
++ {
++ std::set<T>::insert(t1);
++ }
++
++ Set(const T& t1, const T& t2)
++ {
++ std::set<T>::insert(t1);
++ std::set<T>::insert(t2);
++ }
++
++ Set(const T& t1, const T& t2, const T& t3)
++ {
++ std::set<T>::insert(t1);
++ std::set<T>::insert(t2);
++ std::set<T>::insert(t3);
++ }
++
++ Set(const T& t1, const T& t2, const T& t3, const T& t4)
++ {
++ std::set<T>::insert(t1);
++ std::set<T>::insert(t2);
++ std::set<T>::insert(t3);
++ std::set<T>::insert(t4);
++ }
++};
++
++
++template <typename T>
++std::set<T> operator | (const std::set<T>& left, const std::set<T>& right)
++{
++ // Note, we're not using 'set_union' since it affords us no benefit
++ // in efficiency and is more complicated to use given we have sets.
++ std::set<T> result = left;
++ result.insert(right.begin(), right.end());
++ return result;
++}
++
++
++template <typename T>
++std::set<T> operator + (const std::set<T>& left, const T& t)
++{
++ std::set<T> result = left;
++ result.insert(t);
++ return result;
++}
++
++
++template <typename T>
++std::set<T> operator & (const std::set<T>& left, const std::set<T>& right)
++{
++ std::set<T> result;
++ std::set_intersection(
++ left.begin(),
++ left.end(),
++ right.begin(),
++ right.end(),
++ std::inserter(result, result.begin()));
++ return result;
++}
+diff --git a/include/mesos/stout/some.hpp b/include/mesos/stout/some.hpp
+new file mode 100644
+index 0000000..e2f56cc
+--- /dev/null
++++ b/include/mesos/stout/some.hpp
+@@ -0,0 +1,73 @@
++#ifndef __STOUT_SOME_HPP__
++#define __STOUT_SOME_HPP__
++
++#include <stout/option.hpp>
++#include <stout/result.hpp>
++
++// A "some" type that is implicitely convertable to an Option<T> and
++// Result<T> for any T (effectively "syntactic sugar" to make code
++// more readable). The implementation uses cast operators to perform
++// the conversions instead of adding constructors to Option/Result
++// directly. The extra copies involved here can be elided with C++11
++// rvalue references. Furthermore, since in most circumstances a Some
++// will not be needed (an Option<T> or Result<T> can be constructed
++// directly from the value) we don't worry about performance.
++
++template <typename T>
++struct _Some
++{
++ _Some(T _t) : t(_t) {}
++
++ template <typename U>
++ operator Option<U> () const
++ {
++ return Option<U>::some(t);
++ }
++
++ // Give the compiler some help for nested Option<U>.
++ template <template <typename> class S, typename U>
++ operator S<Option<U> > () const
++ {
++ return S<Option<U> >(Option<U>::some(t));
++ }
++
++ template <typename U>
++ operator Result<U> () const
++ {
++ return Result<U>::some(t);
++ }
++
++ // Give the compiler some help for nested Result<U>.
++ template <template <typename> class S, typename U>
++ operator S<Result<U> > () const
++ {
++ return S<Result<U> >(Result<U>::some(t));
++ }
++
++ // Give the compiler some more help to disambiguate the above cast
++ // operators from Option<Result<U>>.
++ template <typename U>
++ operator Option<Result<U> > () const
++ {
++ return Option<Result<U> >::some(t);
++ }
++
++ // Give the compiler some more help to disambiguate the above cast
++ // operators from Result<Option<U>>.
++ template <typename U>
++ operator Result<Option<U> > () const
++ {
++ return Result<Option<U> >::some(t);
++ }
++
++ const T t;
++};
++
++
++template <typename T>
++_Some<T> Some(T t)
++{
++ return _Some<T>(t);
++}
++
++#endif // __STOUT_SOME_HPP__
+diff --git a/include/mesos/stout/stopwatch.hpp b/include/mesos/stout/stopwatch.hpp
+new file mode 100644
+index 0000000..97e3469
+--- /dev/null
++++ b/include/mesos/stout/stopwatch.hpp
+@@ -0,0 +1,77 @@
++#ifndef __STOUT_STOPWATCH_HPP__
++#define __STOUT_STOPWATCH_HPP__
++
++#include <time.h>
++
++#ifdef __MACH__
++#include <mach/clock.h>
++#include <mach/mach.h>
++#endif // __MACH__
++
++#include <sys/time.h>
++
++#include "duration.hpp"
++
++class Stopwatch
++{
++public:
++ Stopwatch()
++ : running(false)
++ {
++ started.tv_sec = 0;
++ started.tv_nsec = 0;
++ stopped.tv_sec = 0;
++ stopped.tv_nsec = 0;
++ }
++
++ void start()
++ {
++ started = now();
++ running = true;
++ }
++
++ void stop()
++ {
++ stopped = now();
++ running = false;
++ }
++
++ Nanoseconds elapsed()
++ {
++ if (!running) {
++ return Nanoseconds(diff(stopped, started));
++ }
++
++ return Nanoseconds(diff(now(), started));
++ }
++
++private:
++ static timespec now()
++ {
++ timespec ts;
++#ifdef __MACH__
++ // OS X does not have clock_gettime, use clock_get_time.
++ clock_serv_t cclock;
++ mach_timespec_t mts;
++ host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock);
++ clock_get_time(cclock, &mts);
++ mach_port_deallocate(mach_task_self(), cclock);
++ ts.tv_sec = mts.tv_sec;
++ ts.tv_nsec = mts.tv_nsec;
++#else
++ clock_gettime(CLOCK_REALTIME, &ts);
++#endif // __MACH__
++ return ts;
++ }
++
++ static uint64_t diff(const timespec& from, const timespec& to)
++ {
++ return ((from.tv_sec - to.tv_sec) * 1000000000LL)
++ + (from.tv_nsec - to.tv_nsec);
++ }
++
++ bool running;
++ timespec started, stopped;
++};
++
++#endif // __STOUT_STOPWATCH_HPP__
+diff --git a/include/mesos/stout/stringify.hpp b/include/mesos/stout/stringify.hpp
+new file mode 100644
+index 0000000..2bb7290
+--- /dev/null
++++ b/include/mesos/stout/stringify.hpp
+@@ -0,0 +1,141 @@
++#ifndef __STOUT_STRINGIFY_HPP__
++#define __STOUT_STRINGIFY_HPP__
++
++#include <stdlib.h> // For 'abort'.
++
++#include <iostream> // For 'std::cerr' and 'std::endl'.
++#include <list>
++#include <map>
++#include <set>
++#include <sstream> // For 'std::ostringstream'.
++#include <string>
++#include <vector>
++
++#include "hashmap.hpp"
++
++template <typename T>
++std::string stringify(T t)
++{
++ std::ostringstream out;
++ out << t;
++ if (!out.good()) {
++ std::cerr << "Failed to stringify!" << t << std::endl;
++ abort();
++ }
++ return out.str();
++}
++
++
++template <>
++inline std::string stringify(bool b)
++{
++ return b ? "true" : "false";
++}
++
++
++template <typename T>
++std::string stringify(const std::set<T>& set)
++{
++ std::ostringstream out;
++ out << "{ ";
++ typename std::set<T>::const_iterator iterator = set.begin();
++ while (iterator != set.end()) {
++ out << stringify(*iterator);
++ if (++iterator != set.end()) {
++ out << ", ";
++ }
++ }
++ out << " }";
++ return out.str();
++}
++
++
++template <typename T>
++std::string stringify(const std::list<T>& list)
++{
++ std::ostringstream out;
++ out << "[ ";
++ typename std::list<T>::const_iterator iterator = list.begin();
++ while (iterator != list.end()) {
++ out << stringify(*iterator);
++ if (++iterator != list.end()) {
++ out << ", ";
++ }
++ }
++ out << " ]";
++ return out.str();
++}
++
++
++template <typename T>
++std::string stringify(const std::vector<T>& vector)
++{
++ std::ostringstream out;
++ out << "[ ";
++ typename std::vector<T>::const_iterator iterator = vector.begin();
++ while (iterator != vector.end()) {
++ out << stringify(*iterator);
++ if (++iterator != vector.end()) {
++ out << ", ";
++ }
++ }
++ out << " ]";
++ return out.str();
++}
++
++
++template <typename K, typename V>
++std::string stringify(const std::map<K, V>& map)
++{
++ std::ostringstream out;
++ out << "{ ";
++ typename std::map<K, V>::const_iterator iterator = map.begin();
++ while (iterator != map.end()) {
++ out << stringify(iterator->first);
++ out << ": ";
++ out << stringify(iterator->second);
++ if (++iterator != map.end()) {
++ out << ", ";
++ }
++ }
++ out << " }";
++ return out.str();
++}
++
++
++template <typename T>
++std::string stringify(const hashset<T>& set)
++{
++ std::ostringstream out;
++ out << "{ ";
++ typename hashset<T>::const_iterator iterator = set.begin();
++ while (iterator != set.end()) {
++ out << stringify(*iterator);
++ if (++iterator != set.end()) {
++ out << ", ";
++ }
++ }
++ out << " }";
++ return out.str();
++}
++
++
++template <typename K, typename V>
++std::string stringify(const hashmap<K, V>& map)
++{
++ std::ostringstream out;
++ out << "{ ";
++ typename hashmap<K, V>::const_iterator iterator = map.begin();
++ while (iterator != map.end()) {
++ out << stringify(iterator->first);
++ out << ": ";
++ out << stringify(iterator->second);
++ if (++iterator != map.end()) {
++ out << ", ";
++ }
++ }
++ out << " }";
++ return out.str();
++}
++
++#endif // __STOUT_STRINGIFY_HPP__
+diff --git a/include/mesos/stout/strings.hpp b/include/mesos/stout/strings.hpp
+new file mode 100644
+index 0000000..46a0a26
+--- /dev/null
++++ b/include/mesos/stout/strings.hpp
+@@ -0,0 +1,262 @@
++#ifndef __STOUT_STRINGS_HPP__
++#define __STOUT_STRINGS_HPP__
++
++#include <algorithm>
++#include <string>
++#include <map>
++#include <vector>
++
++#include "foreach.hpp"
++#include "format.hpp"
++#include "stringify.hpp"
++
++namespace strings {
++
++// Flags indicating how remove should operate.
++enum Mode {
++ PREFIX,
++ SUFFIX,
++ ANY
++};
++
++
++inline std::string remove(
++ const std::string& from,
++ const std::string& substring,
++ Mode mode = ANY)
++{
++ std::string result = from;
++
++ if (mode == PREFIX) {
++ if (from.find(substring) == 0) {
++ result = from.substr(substring.size());
++ }
++ } else if (mode == SUFFIX) {
++ if (from.rfind(substring) == from.size() - substring.size()) {
++ result = from.substr(0, from.size() - substring.size());
++ }
++ } else {
++ size_t index;
++ while ((index = result.find(substring)) != std::string::npos) {
++ result = result.erase(index, substring.size());
++ }
++ }
++
++ return result;
++}
++
++
++inline std::string trim(
++ const std::string& from,
++ const std::string& chars = " \t\n\r")
++{
++ size_t start = from.find_first_not_of(chars);
++ size_t end = from.find_last_not_of(chars);
++ if (start == std::string::npos) { // Contains only characters in chars.
++ return "";
++ }
++
++ return from.substr(start, end + 1 - start);
++}
++
++
++// Replaces all the occurrences of the 'from' string with the 'to' string.
++inline std::string replace(
++ const std::string& s,
++ const std::string& from,
++ const std::string& to)
++{
++ std::string result = s;
++ size_t index = 0;
++
++ if (from.empty()) {
++ return result;
++ }
++
++ while ((index = result.find(from, index)) != std::string::npos) {
++ result.replace(index, from.length(), to);
++ index += to.length();
++ }
++ return result;
++}
++
++
++// Tokenizes the string using the delimiters.
++// Empty tokens will not be included in the result.
++inline std::vector<std::string> tokenize(
++ const std::string& s,
++ const std::string& delims)
++{
++ size_t offset = 0;
++ std::vector<std::string> tokens;
++
++ while (true) {
++ size_t i = s.find_first_not_of(delims, offset);
++ if (std::string::npos == i) {
++ break;
++ }
++
++ size_t j = s.find_first_of(delims, i);
++ if (std::string::npos == j) {
++ tokens.push_back(s.substr(i));
++ offset = s.length();
++ continue;
++ }
++
++ tokens.push_back(s.substr(i, j - i));
++ offset = j;
++ }
++ return tokens;
++}
++
++
++// Splits the string using the provided delimiters.
++// Empty tokens are allowed in the result.
++inline std::vector<std::string> split(
++ const std::string& s,
++ const std::string& delims)
++{
++ std::vector<std::string> tokens;
++ size_t offset = 0;
++ size_t next = 0;
++
++ while (true) {
++ next = s.find_first_of(delims, offset);
++ if (next == std::string::npos) {
++ tokens.push_back(s.substr(offset));
++ break;
++ }
++
++ tokens.push_back(s.substr(offset, next - offset));
++ offset = next + 1;
++ }
++ return tokens;
++}
++
++
++// Returns a map of strings to strings based on calling tokenize
++// twice. All non-pairs are discarded. For example:
++//
++// pairs("foo=1;bar=2;baz;foo=3;bam=1=2", ";&", "=")
++//
++// Would return a map with the following:
++// bar: ["2"]
++// foo: ["1", "3"]
++inline std::map<std::string, std::vector<std::string> > pairs(
++ const std::string& s,
++ const std::string& delims1,
++ const std::string& delims2)
++{
++ std::map<std::string, std::vector<std::string> > result;
++
++ const std::vector<std::string>& tokens = tokenize(s, delims1);
++ foreach (const std::string& token, tokens) {
++ const std::vector<std::string>& pairs = tokenize(token, delims2);
++ if (pairs.size() == 2) {
++ result[pairs[0]].push_back(pairs[1]);
++ }
++ }
++
++ return result;
++}
++
++
++inline std::string join(const std::string& separator,
++ const std::string& s1,
++ const std::string& s2)
++{
++ return s1 + separator + s2;
++}
++
++
++inline std::string join(const std::string& separator,
++ const std::string& s1,
++ const std::string& s2,
++ const std::string& s3)
++{
++ return s1 + separator + s2 + separator + s3;
++}
++
++
++inline std::string join(const std::string& separator,
++ const std::string& s1,
++ const std::string& s2,
++ const std::string& s4,
++ const std::string& s3)
++{
++ return s1 + separator + s2 + separator + s3 + separator + s4;
++}
++
++
++// Use duck-typing to join any iterable.
++template <typename Iterable>
++inline std::string join(const std::string& separator, const Iterable& i)
++{
++ std::string result;
++ typename Iterable::const_iterator iterator = i.begin();
++ while (iterator != i.end()) {
++ result += stringify(*iterator);
++ if (++iterator != i.end()) {
++ result += separator;
++ }
++ }
++ return result;
++}
++
++
++inline bool checkBracketsMatching(
++ const std::string& s,
++ const char openBracket,
++ const char closeBracket)
++{
++ int count = 0;
++ for (size_t i = 0; i < s.length(); i++) {
++ if (s[i] == openBracket) {
++ count++;
++ } else if (s[i] == closeBracket) {
++ count--;
++ }
++ if (count < 0) {
++ return false;
++ }
++ }
++ return count == 0;
++}
++
++
++inline bool startsWith(const std::string& s, const std::string& prefix)
++{
++ return s.find(prefix) == 0;
++}
++
++
++inline bool endsWith(const std::string& s, const std::string& suffix)
++{
++ return s.rfind(suffix) == s.length() - suffix.length();
++}
++
++
++inline bool contains(const std::string& s, const std::string& substr)
++{
++ return s.find(substr) != std::string::npos;
++}
++
++
++inline std::string lower(const std::string& s)
++{
++ std::string result = s;
++ std::transform(result.begin(), result.end(), result.begin(), ::tolower);
++ return result;
++}
++
++
++inline std::string upper(const std::string& s)
++{
++ std::string result = s;
++ std::transform(result.begin(), result.end(), result.begin(), ::toupper);
++ return result;
++}
++
++} // namespaces strings {
++
++#endif // __STOUT_STRINGS_HPP__
+diff --git a/include/mesos/stout/thread.hpp b/include/mesos/stout/thread.hpp
+new file mode 100644
+index 0000000..c5dbe79
+--- /dev/null
++++ b/include/mesos/stout/thread.hpp
+@@ -0,0 +1,49 @@
++#ifndef __STOUT_THREAD_HPP__
++#define __STOUT_THREAD_HPP__
++
++#include <pthread.h>
++#include <stdio.h> // For perror.
++#include <stdlib.h> // For abort.
++
++template <typename T>
++struct ThreadLocal
++{
++ ThreadLocal()
++ {
++ if (pthread_key_create(&key, NULL) != 0) {
++ perror("Failed to create thread local, pthread_key_create");
++ abort();
++ }
++ }
++
++ ThreadLocal<T>& operator = (T* t)
++ {
++ if (pthread_setspecific(key, t) != 0) {
++ perror("Failed to set thread local, pthread_setspecific");
++ abort();
++ }
++ return *this;
++ }
++
++ operator T* () const
++ {
++ return reinterpret_cast<T*>(pthread_getspecific(key));
++ }
++
++ T* operator -> () const
++ {
++ return reinterpret_cast<T*>(pthread_getspecific(key));
++ }
++
++private:
++ // Not expecting any other operators to be used (and the rest?).
++ bool operator * (const ThreadLocal<T>&) const;
++ bool operator == (const ThreadLocal<T>&) const;
++ bool operator != (const ThreadLocal<T>&) const;
++ bool operator < (const ThreadLocal<T>&) const;
++ bool operator > (const ThreadLocal<T>&) const;
++
++ pthread_key_t key;
++};
++
++#endif // __STOUT_THREAD_HPP__
+diff --git a/include/mesos/stout/try.hpp b/include/mesos/stout/try.hpp
+new file mode 100644
+index 0000000..787bffd
+--- /dev/null
++++ b/include/mesos/stout/try.hpp
+@@ -0,0 +1,88 @@
++#ifndef __STOUT_TRY_HPP__
++#define __STOUT_TRY_HPP__
++
++#include <assert.h>
++#include <stdlib.h> // For abort.
++
++#include <iostream>
++#include <string>
++
++
++template <typename T>
++class Try
++{
++public:
++ static Try<T> some(const T& t)
++ {
++ return Try<T>(SOME, new T(t));
++ }
++
++ static Try<T> error(const std::string& message)
++ {
++ return Try<T>(ERROR, NULL, message);
++ }
++
++ Try(const T& _t) : state(SOME), t(new T(_t)) {}
++
++ Try(const Try<T>& that)
++ {
++ state = that.state;
++ if (that.t != NULL) {
++ t = new T(*that.t);
++ } else {
++ t = NULL;
++ }
++ message = that.message;
++ }
++
++ ~Try()
++ {
++ delete t;
++ }
++
++ Try<T>& operator = (const Try<T>& that)
++ {
++ if (this != &that) {
++ delete t;
++ state = that.state;
++ if (that.t != NULL) {
++ t = new T(*that.t);
++ } else {
++ t = NULL;
++ }
++ message = that.message;
++ }
++
++ return *this;
++ }
++
++ bool isSome() const { return state == SOME; }
++ bool isError() const { return state == ERROR; }
++
++ T get() const
++ {
++ if (state != SOME) {
++ std::cerr << "Try::get() but state == ERROR: " << error() << std::endl;
++ abort();
++ }
++ return *t;
++ }
++
++ std::string error() const { assert(state == ERROR); return message; }
++
++private:
++ enum State {
++ SOME,
++ ERROR
++ };
++
++ Try(State _state, T* _t = NULL, const std::string& _message = "")
++ : state(_state), t(_t), message(_message) {}
++
++ State state;
++ T* t;
++ std::string message;
++};
++
++
++#endif // __STOUT_TRY_HPP__
+diff --git a/include/mesos/stout/utils.hpp b/include/mesos/stout/utils.hpp
+new file mode 100644
+index 0000000..0f4bba2
+--- /dev/null
++++ b/include/mesos/stout/utils.hpp
+@@ -0,0 +1,11 @@
++#ifndef __STOUT_UTILS_HPP__
++#define __STOUT_UTILS_HPP__
++
++namespace utils {
++
++template <typename T>
++T copy(const T& t) { return t; }
++
++} // namespace utils {
++
++#endif // __STOUT_UTILS_HPP__
+diff --git a/include/mesos/stout/uuid.hpp b/include/mesos/stout/uuid.hpp
+new file mode 100644
+index 0000000..c6c290d
+--- /dev/null
++++ b/include/mesos/stout/uuid.hpp
+@@ -0,0 +1,54 @@
++#ifndef __STOUT_UUID_HPP__
++#define __STOUT_UUID_HPP__
++
++#include <assert.h>
++
++#include <sstream>
++#include <string>
++
++#include <boost/uuid/uuid.hpp>
++#include <boost/uuid/uuid_io.hpp>
++#include <boost/uuid/uuid_generators.hpp>
++
++struct UUID : boost::uuids::uuid
++{
++public:
++ static UUID random()
++ {
++ return UUID(boost::uuids::random_generator()());
++ }
++
++ static UUID fromBytes(const std::string& s)
++ {
++ boost::uuids::uuid uuid;
++ memcpy(&uuid, s.data(), s.size());
++ return UUID(uuid);
++ }
++
++ static UUID fromString(const std::string& s)
++ {
++ boost::uuids::uuid uuid;
++ std::istringstream in(s);
++ in >> uuid;
++ return UUID(uuid);
++ }
++
++ std::string toBytes() const
++ {
++ assert(sizeof(data) == size());
++ return std::string(reinterpret_cast<const char*>(data), sizeof(data));
++ }
++
++ std::string toString() const
++ {
++ std::ostringstream out;
++ out << *this;
++ return out.str();
++ }
++
++private:
++ explicit UUID(const boost::uuids::uuid& uuid)
++ : boost::uuids::uuid(uuid) {}
++};
++
++#endif // __STOUT_UUID_HPP__
+diff --git a/src/libprocess/config.hpp b/src/libprocess/config.hpp
+new file mode 100644
+index 0000000..cbaf41d
+--- /dev/null
++++ b/src/libprocess/config.hpp
+@@ -0,0 +1,38 @@
++#ifndef CONFIG_HPP
++#define CONFIG_HPP
++
++#ifdef __sun__
++#define gethostbyname2(name, _) gethostbyname(name)
++#ifndef MSG_NOSIGNAL
++#define MSG_NOSIGNAL 0
++#endif
++#ifndef SOL_TCP
++#define SOL_TCP IPPROTO_TCP
++#endif
++#ifndef MAP_32BIT
++#define MAP_32BIT 0
++#endif
++#endif /* __sun__ */
++
++#ifdef __APPLE__
++#ifndef MAP_ANONYMOUS
++#define MAP_ANONYMOUS MAP_ANON
++#endif
++#ifndef MSG_NOSIGNAL
++#define MSG_NOSIGNAL 0
++#endif
++#ifndef SOL_TCP
++#define SOL_TCP IPPROTO_TCP
++#endif
++#ifndef MAP_32BIT
++#define MAP_32BIT 0
++#endif
++#endif /* __APPLE__ */
++
++#ifdef __linux__
++#ifndef MAP_32BIT
++#define MAP_32BIT 0
++#endif
++#endif /* __linux__ */
++
++#endif /* CONFIG_HPP */
+diff --git a/src/libprocess/decoder.hpp b/src/libprocess/decoder.hpp
+new file mode 100644
+index 0000000..be410d9
+--- /dev/null
++++ b/src/libprocess/decoder.hpp
+@@ -0,0 +1,437 @@
++#ifndef __DECODER_HPP__
++#define __DECODER_HPP__
++
++#include <http_parser.h>
++
++#include <deque>
++#include <string>
++#include <vector>
++
++#include <process/http.hpp>
++#include <process/socket.hpp>
++
++#include <stout/foreach.hpp>
++#include <stout/gzip.hpp>
++#include <stout/try.hpp>
++
++
++// TODO(bmahler): Upgrade our http_parser to the latest version.
++namespace process {
++
++// TODO: Make DataDecoder abstract and make RequestDecoder a concrete subclass.
++class DataDecoder
++{
++public:
++ DataDecoder(const Socket& _s)
++ : s(_s), failure(false), request(NULL)
++ {
++ settings.on_message_begin = &DataDecoder::on_message_begin;
++ settings.on_header_field = &DataDecoder::on_header_field;
++ settings.on_header_value = &DataDecoder::on_header_value;
++ settings.on_url = &DataDecoder::on_url;
++ settings.on_body = &DataDecoder::on_body;
++ settings.on_headers_complete = &DataDecoder::on_headers_complete;
++ settings.on_message_complete = &DataDecoder::on_message_complete;
++
++#if !(HTTP_PARSER_VERSION_MAJOR >=2)
++ settings.on_path = &DataDecoder::on_path;
++ settings.on_fragment = &DataDecoder::on_fragment;
++ settings.on_query_string = &DataDecoder::on_query_string;
++#endif
++
++ http_parser_init(&parser, HTTP_REQUEST);
++
++ parser.data = this;
++ }
++
++ std::deque<http::Request*> decode(const char* data, size_t length)
++ {
++ size_t parsed = http_parser_execute(&parser, &settings, data, length);
++
++ if (parsed != length) {
++ failure = true;
++ }
++
++ if (!requests.empty()) {
++ std::deque<http::Request*> result = requests;
++ requests.clear();
++ return result;
++ }
++
++ return std::deque<http::Request*>();
++ }
++
++ bool failed() const
++ {
++ return failure;
++ }
++
++ Socket socket() const
++ {
++ return s;
++ }
++
++private:
++ static int on_message_begin(http_parser* p)
++ {
++ DataDecoder* decoder = (DataDecoder*) p->data;
++
++ assert(!decoder->failure);
++
++ decoder->header = HEADER_FIELD;
++ decoder->field.clear();
++ decoder->value.clear();
++ decoder->query.clear();
++
++ assert(decoder->request == NULL);
++ decoder->request = new http::Request();
++ decoder->request->headers.clear();
++ decoder->request->method.clear();
++ decoder->request->path.clear();
++ decoder->request->url.clear();
++ decoder->request->fragment.clear();
++ decoder->request->query.clear();
++ decoder->request->body.clear();
++
++ return 0;
++ }
++
++ static int on_headers_complete(http_parser* p)
++ {
++ DataDecoder* decoder = (DataDecoder*) p->data;
++
++ // Add final header.
++ decoder->request->headers[decoder->field] = decoder->value;
++ decoder->field.clear();
++ decoder->value.clear();
++
++ decoder->request->method = http_method_str((http_method) decoder->parser.method);
++ decoder->request->keepAlive = http_should_keep_alive(&decoder->parser);
++ return 0;
++ }
++
++ static int on_message_complete(http_parser* p)
++ {
++ DataDecoder* decoder = (DataDecoder*) p->data;
++// std::cout << "http::Request:" << std::endl;
++// std::cout << " method: " << decoder->request->method << std::endl;
++// std::cout << " path: " << decoder->request->path << std::endl;
++ // Parse the query key/values.
++ Try<std::string> decoded = http::decode(decoder->query);
++ if (decoded.isError()) {
++ return 1;
++ }
++ decoder->request->query = http::query::parse(decoded.get());
++
++ Option<std::string> encoding =
++ decoder->request->headers.get("Content-Encoding");
++ if (encoding.isSome() && encoding.get() == "gzip") {
++ Try<std::string> decompressed = gzip::decompress(decoder->request->body);
++ if (decompressed.isError()) {
++ return 1;
++ }
++ decoder->request->body = decompressed.get();
++ decoder->request->headers["Content-Length"] =
++ decoder->request->body.length();
++ }
++
++ decoder->requests.push_back(decoder->request);
++ decoder->request = NULL;
++ return 0;
++ }
++
++ static int on_header_field(http_parser* p, const char* data, size_t length)
++ {
++ DataDecoder* decoder = (DataDecoder*) p->data;
++ assert(decoder->request != NULL);
++
++ if (decoder->header != HEADER_FIELD) {
++ decoder->request->headers[decoder->field] = decoder->value;
++ decoder->field.clear();
++ decoder->value.clear();
++ }
++
++ decoder->field.append(data, length);
++ decoder->header = HEADER_FIELD;
++
++ return 0;
++ }
++
++ static int on_header_value(http_parser* p, const char* data, size_t length)
++ {
++ DataDecoder* decoder = (DataDecoder*) p->data;
++ assert(decoder->request != NULL);
++ decoder->value.append(data, length);
++ decoder->header = HEADER_VALUE;
++ return 0;
++ }
++
++ static int on_url(http_parser* p, const char* data, size_t length)
++ {
++ DataDecoder* decoder = (DataDecoder*) p->data;
++ assert(decoder->request != NULL);
++ decoder->request->url.append(data, length);
++ int ret = 0;
++
++#if (HTTP_PARSER_VERSION_MAJOR >=2)
++ // reworked parsing for version 2.0 &>
++ http_parser_url tUrlData;
++ ret = http_parser_parse_url(data, length, 0, &tUrlData);
++
++ if (tUrlData.field_set & (1<<UF_PATH))
++ decoder->request->path.append(data+tUrlData.field_data[UF_PATH].off, tUrlData.field_data[UF_PATH].len);
++
++ if (tUrlData.field_set & (1<<UF_FRAGMENT))
++ decoder->request->fragment.append(data+tUrlData.field_data[UF_FRAGMENT].off, tUrlData.field_data[UF_FRAGMENT].len);
++
++ if (tUrlData.field_set & (1<<UF_QUERY))
++ decoder->query.append(data+tUrlData.field_data[UF_QUERY].off, tUrlData.field_data[UF_QUERY].len);
++#endif
++
++ return ret;
++ }
++
++#if !(HTTP_PARSER_VERSION_MAJOR >=2)
++ static int on_path(http_parser* p, const char* data, size_t length)
++ {
++ DataDecoder* decoder = (DataDecoder*) p->data;
++ assert(decoder->request != NULL);
++ decoder->request->path.append(data, length);
++ return 0;
++ }
++
++ static int on_query_string(http_parser* p, const char* data, size_t length)
++ {
++ DataDecoder* decoder = (DataDecoder*) p->data;
++ assert(decoder->request != NULL);
++ decoder->query.append(data, length);
++ return 0;
++ }
++
++ static int on_fragment(http_parser* p, const char* data, size_t length)
++ {
++ DataDecoder* decoder = (DataDecoder*) p->data;
++ assert(decoder->request != NULL);
++ decoder->request->fragment.append(data, length);
++ return 0;
++ }
++#endif
++
++ static int on_body(http_parser* p, const char* data, size_t length)
++ {
++ DataDecoder* decoder = (DataDecoder*) p->data;
++ assert(decoder->request != NULL);
++ decoder->request->body.append(data, length);
++ return 0;
++ }
++
++ const Socket s; // The socket this decoder is associated with.
++
++ bool failure;
++
++ http_parser parser;
++ http_parser_settings settings;
++
++ enum {
++ HEADER_FIELD,
++ HEADER_VALUE
++ } header;
++
++ std::string field;
++ std::string value;
++ std::string query;
++
++ http::Request* request;
++
++ std::deque<http::Request*> requests;
++};
++
++
++class ResponseDecoder
++{
++public:
++ ResponseDecoder()
++ : failure(false), header(HEADER_FIELD), response(NULL)
++ {
++ settings.on_message_begin = &ResponseDecoder::on_message_begin;
++ settings.on_header_field = &ResponseDecoder::on_header_field;
++ settings.on_header_value = &ResponseDecoder::on_header_value;
++ settings.on_url = &ResponseDecoder::on_url;
++ settings.on_body = &ResponseDecoder::on_body;
++ settings.on_headers_complete = &ResponseDecoder::on_headers_complete;
++ settings.on_message_complete = &ResponseDecoder::on_message_complete;
++
++#if !(HTTP_PARSER_VERSION_MAJOR >=2)
++ settings.on_path = &ResponseDecoder::on_path;
++ settings.on_fragment = &ResponseDecoder::on_fragment;
++ settings.on_query_string = &ResponseDecoder::on_query_string;
++#endif
++
++ http_parser_init(&parser, HTTP_RESPONSE);
++
++ parser.data = this;
++ }
++
++ std::deque<http::Response*> decode(const char* data, size_t length)
++ {
++ size_t parsed = http_parser_execute(&parser, &settings, data, length);
++
++ if (parsed != length) {
++ failure = true;
++ }
++
++ if (!responses.empty()) {
++ std::deque<http::Response*> result = responses;
++ responses.clear();
++ return result;
++ }
++
++ return std::deque<http::Response*>();
++ }
++
++ bool failed() const
++ {
++ return failure;
++ }
++
++private:
++ static int on_message_begin(http_parser* p)
++ {
++ ResponseDecoder* decoder = (ResponseDecoder*) p->data;
++
++ assert(!decoder->failure);
++
++ decoder->header = HEADER_FIELD;
++ decoder->field.clear();
++ decoder->value.clear();
++
++ assert(decoder->response == NULL);
++ decoder->response = new http::Response();
++ decoder->response->status.clear();
++ decoder->response->headers.clear();
++ decoder->response->type = http::Response::BODY;
++ decoder->response->body.clear();
++ decoder->response->path.clear();
++
++ return 0;
++ }
++
++ static int on_headers_complete(http_parser* p)
++ {
++ ResponseDecoder* decoder = (ResponseDecoder*) p->data;
++
++ // Add final header.
++ decoder->response->headers[decoder->field] = decoder->value;
++ decoder->field.clear();
++ decoder->value.clear();
++
++ return 0;
++ }
++
++ static int on_message_complete(http_parser* p)
++ {
++ ResponseDecoder* decoder = (ResponseDecoder*) p->data;
++
++ // Get the response status string.
++ if (http::statuses.contains(decoder->parser.status_code)) {
++ decoder->response->status = http::statuses[decoder->parser.status_code];
++ } else {
++ decoder->failure = true;
++ return 1;
++ }
++
++ // We can only provide the gzip encoding.
++ Option<std::string> encoding =
++ decoder->response->headers.get("Content-Encoding");
++ if (encoding.isSome() && encoding.get() == "gzip") {
++ Try<std::string> decompressed = gzip::decompress(decoder->response->body);
++ if (decompressed.isError()) {
++ decoder->failure = true;
++ return 1;
++ }
++ decoder->response->body = decompressed.get();
++ decoder->response->headers["Content-Length"] =
++ decoder->response->body.length();
++ }
++
++ decoder->responses.push_back(decoder->response);
++ decoder->response = NULL;
++ return 0;
++ }
++
++ static int on_header_field(http_parser* p, const char* data, size_t length)
++ {
++ ResponseDecoder* decoder = (ResponseDecoder*) p->data;
++ assert(decoder->response != NULL);
++
++ if (decoder->header != HEADER_FIELD) {
++ decoder->response->headers[decoder->field] = decoder->value;
++ decoder->field.clear();
++ decoder->value.clear();
++ }
++
++ decoder->field.append(data, length);
++ decoder->header = HEADER_FIELD;
++
++ return 0;
++ }
++
++ static int on_header_value(http_parser* p, const char* data, size_t length)
++ {
++ ResponseDecoder* decoder = (ResponseDecoder*) p->data;
++ assert(decoder->response != NULL);
++ decoder->value.append(data, length);
++ decoder->header = HEADER_VALUE;
++ return 0;
++ }
++
++ static int on_path(http_parser* p, const char* data, size_t length)
++ {
++ return 0;
++ }
++
++ static int on_url(http_parser* p, const char* data, size_t length)
++ {
++ return 0;
++ }
++
++ static int on_query_string(http_parser* p, const char* data, size_t length)
++ {
++ return 0;
++ }
++
++ static int on_fragment(http_parser* p, const char* data, size_t length)
++ {
++ return 0;
++ }
++
++ static int on_body(http_parser* p, const char* data, size_t length)
++ {
++ ResponseDecoder* decoder = (ResponseDecoder*) p->data;
++ assert(decoder->response != NULL);
++ decoder->response->body.append(data, length);
++ return 0;
++ }
++
++ bool failure;
++
++ http_parser parser;
++ http_parser_settings settings;
++
++ enum {
++ HEADER_FIELD,
++ HEADER_VALUE
++ } header;
++
++ std::string field;
++ std::string value;
++
++ http::Response* response;
++
++ std::deque<http::Response*> responses;
++};
++
++
++} // namespace process {
++
++#endif // __DECODER_HPP__
+diff --git a/src/libprocess/encoder.hpp b/src/libprocess/encoder.hpp
+new file mode 100644
+index 0000000..55aba22
+--- /dev/null
++++ b/src/libprocess/encoder.hpp
+@@ -0,0 +1,262 @@
++#ifndef __ENCODER_HPP__
++#define __ENCODER_HPP__
++
++#include <map>
++#include <sstream>
++
++#include <process/http.hpp>
++#include <process/process.hpp>
++
++#include <stout/foreach.hpp>
++#include <stout/gzip.hpp>
++#include <stout/hashmap.hpp>
++#include <stout/numify.hpp>
++#include <stout/os.hpp>
++
++// NOTE: We forward declare "ev_loop" and "ev_io" here because,
++// on OSX, including "ev.h" causes conflict with "EV_ERROR" declared
++// in "/usr/include/sys/event.h".
++struct ev_loop;
++struct ev_io;
++
++namespace process {
++
++const uint32_t GZIP_MINIMUM_BODY_LENGTH = 1024;
++
++typedef void (*Sender)(struct ev_loop*, ev_io*, int);
++
++extern void send_data(struct ev_loop*, ev_io*, int);
++extern void send_file(struct ev_loop*, ev_io*, int);
++
++
++class Encoder
++{
++public:
++ Encoder(const Socket& _s) : s(_s) {}
++ virtual ~Encoder() {}
++
++ virtual Sender sender() = 0;
++
++ Socket socket() const
++ {
++ return s;
++ }
++
++private:
++ const Socket s; // The socket this encoder is associated with.
++};
++
++
++class DataEncoder : public Encoder
++{
++public:
++ DataEncoder(const Socket& s, const std::string& _data)
++ : Encoder(s), data(_data), index(0) {}
++
++ virtual ~DataEncoder() {}
++
++ virtual Sender sender()
++ {
++ return send_data;
++ }
++
++ virtual const char* next(size_t* length)
++ {
++ size_t temp = index;
++ index = data.size();
++ *length = data.size() - temp;
++ return data.data() + temp;
++ }
++
++ virtual void backup(size_t length)
++ {
++ if (index >= length) {
++ index -= length;
++ }
++ }
++
++ virtual size_t remaining() const
++ {
++ return data.size() - index;
++ }
++
++private:
++ const std::string data;
++ size_t index;
++};
++
++
++class MessageEncoder : public DataEncoder
++{
++public:
++ MessageEncoder(const Socket& s, Message* _message)
++ : DataEncoder(s, encode(_message)), message(_message) {}
++
++ virtual ~MessageEncoder()
++ {
++ if (message != NULL) {
++ delete message;
++ }
++ }
++
++ static std::string encode(Message* message)
++ {
++ if (message != NULL) {
++ std::ostringstream out;
++
++ out << "POST /" << message->to.id << "/" << message->name
++ << " HTTP/1.0\r\n"
++ << "User-Agent: libprocess/" << message->from << "\r\n"
++ << "Connection: Keep-Alive\r\n";
++
++ if (message->body.size() > 0) {
++ out << "Transfer-Encoding: chunked\r\n\r\n"
++ << std::hex << message->body.size() << "\r\n";
++ out.write(message->body.data(), message->body.size());
++ out << "\r\n"
++ << "0\r\n"
++ << "\r\n";
++ } else {
++ out << "\r\n";
++ }
++
++ return out.str();
++ }
++ }
++
++private:
++ Message* message;
++};
++
++
++class HttpResponseEncoder : public DataEncoder
++{
++public:
++ HttpResponseEncoder(
++ const Socket& s,
++ const http::Response& response,
++ const http::Request& request)
++ : DataEncoder(s, encode(response, request)) {}
++
++ static std::string encode(
++ const http::Response& response,
++ const http::Request& request)
++ {
++ std::ostringstream out;
++
++ // TODO(benh): Check version?
++
++ out << "HTTP/1.1 " << response.status << "\r\n";
++
++ hashmap<std::string, std::string> headers = response.headers;
++
++ // HTTP 1.1 requires the "Date" header. In the future once we
++ // start checking the version (above) then we can conditionally
++ // add this header, but for now, we always do.
++ time_t rawtime;
++ time(&rawtime);
++
++ char date[256];
++
++ // TODO(benh): Check return code of strftime!
++ strftime(date, 256, "%a, %d %b %Y %H:%M:%S GMT", gmtime(&rawtime));
++
++ headers["Date"] = date;
++
++ // Should we compress this response?
++ std::string body = response.body;
++
++ if (response.type == http::Response::BODY &&
++ response.body.length() >= GZIP_MINIMUM_BODY_LENGTH &&
++ !headers.contains("Content-Encoding") &&
++ request.accepts("gzip")) {
++ Try<std::string> compressed = gzip::compress(body);
++ if (compressed.isError()) {
++ LOG(WARNING) << "Failed to gzip response body: " << compressed.error();
++ } else {
++ body = compressed.get();
++ headers["Content-Length"] = stringify(body.length());
++ headers["Content-Encoding"] = "gzip";
++ }
++ }
++
++ foreachpair (const std::string& key, const std::string& value, headers) {
++ out << key << ": " << value << "\r\n";
++ }
++
++ // Add a Content-Length header if the response is of type "none"
++ // or "body" and no Content-Length header has been supplied.
++ if (response.type == http::Response::NONE &&
++ !headers.contains("Content-Length")) {
++ out << "Content-Length: 0\r\n";
++ } else if (response.type == http::Response::BODY &&
++ !headers.contains("Content-Length")) {
++ out << "Content-Length: " << body.size() << "\r\n";
++ }
++
++ // Use a CRLF to mark end of headers.
++ out << "\r\n";
++
++ // Add the body if necessary.
++ if (response.type == http::Response::BODY) {
++ // If the Content-Length header was supplied, only write as much data
++ // as the length specifies.
++ Result<uint32_t> length = numify<uint32_t>(headers.get("Content-Length"));
++ if (length.isSome() && length.get() <= body.length()) {
++ out.write(body.data(), length.get());
++ } else {
++ out.write(body.data(), body.size());
++ }
++ }
++
++ return out.str();
++ }
++};
++
++
++class FileEncoder : public Encoder
++{
++public:
++ FileEncoder(const Socket& s, int _fd, size_t _size)
++ : Encoder(s), fd(_fd), size(_size), index(0) {}
++
++ virtual ~FileEncoder()
++ {
++ os::close(fd);
++ }
++
++ virtual Sender sender()
++ {
++ return send_file;
++ }
++
++ virtual int next(off_t* offset, size_t* length)
++ {
++ off_t temp = index;
++ index = size;
++ *offset = temp;
++ *length = size - temp;
++ return fd;
++ }
++
++ virtual void backup(size_t length)
++ {
++ if (index >= length) {
++ index -= length;
++ }
++ }
++
++ virtual size_t remaining() const
++ {
++ return size - index;
++ }
++
++private:
++ int fd;
++ size_t size;
++ off_t index;
++};
++
++} // namespace process {
++
++#endif // __ENCODER_HPP__
+diff --git a/src/libprocess/examples/example.cpp b/src/libprocess/examples/example.cpp
+new file mode 100644
+index 0000000..3fb4ef5
+--- /dev/null
++++ b/src/libprocess/examples/example.cpp
+@@ -0,0 +1,121 @@
++#include <iostream>
++#include <sstream>
++
++#include <process/defer.hpp>
++#include <process/dispatch.hpp>
++#include <process/future.hpp>
++#include <process/http.hpp>
++#include <process/process.hpp>
++
++using namespace process;
++
++using namespace process::http;
++
++using std::string;
++
++class MyProcess : public Process<MyProcess>
++{
++public:
++ MyProcess() {}
++ virtual ~MyProcess() {}
++
++ Future<int> func1()
++ {
++ promise.future().onAny(
++ defer([=] (const Future<int>& future) {
++ terminate(self());
++ }));
++ return promise.future();
++ }
++
++ void func2(int i)
++ {
++ promise.set(i);
++ }
++
++ Future<Response> vars(const Request& request)
++ {
++ string body = "... vars here ...";
++ OK response;
++ response.headers["Content-Type"] = "text/plain";
++ std::ostringstream out;
++ out << body.size();
++ response.headers["Content-Length"] = out.str();
++ response.body = body;
++ return response;
++ }
++
++ void stop(const UPID& from, const string& body)
++ {
++ terminate(self());
++ }
++
++protected:
++ virtual void initialize()
++ {
++// route("/vars", &MyProcess::vars);
++ route("/vars", [=] (const Request& request) {
++ string body = "... vars here ...";
++ OK response;
++ response.headers["Content-Type"] = "text/plain";
++ std::ostringstream out;
++ out << body.size();
++ response.headers["Content-Length"] = out.str();
++ response.body = body;
++ return response;
++ });
++
++// install("stop", &MyProcess::stop);
++ install("stop", [=] (const UPID& from, const string& body) {
++ terminate(self());
++ });
++ }
++
++private:
++ Promise<int> promise;
++};
++
++
++int main(int argc, char** argv)
++{
++ MyProcess process;
++ PID<MyProcess> pid = spawn(&process);
++
++ PID<> pid2 = pid;
++
++// --------------------------------------
++
++// Future<int> future = dispatch(pid, &MyProcess::func1);
++// dispatch(pid, &MyProcess::func2, 42);
++
++// std::cout << future.get() << std::endl;
++
++// post(pid, "stop");
++
++// --------------------------------------
++
++// Promise<bool> p;
++
++// dispatch(pid, &MyProcess::func1)
++// .then([=, &p] (int i) {
++// p.set(i == 42);
++// return p.future();
++// })
++// .then([=] (bool b) {
++// if (b) {
++// post(pid, "stop");
++// }
++// return true; // No Future<void>.
++// });
++
++// dispatch(pid, &MyProcess::func2, 42);
++
++// --------------------------------------
++
++ dispatch(pid, &MyProcess::func1);
++ dispatch(pid, &MyProcess::func2, 42);
++
++
++ wait(pid);
++ return 0;
++}
+diff --git a/src/libprocess/fatal.cpp b/src/libprocess/fatal.cpp
+new file mode 100644
+index 0000000..b2934e0
+--- /dev/null
++++ b/src/libprocess/fatal.cpp
+@@ -0,0 +1,26 @@
++#include <stdarg.h>
++#include <stdio.h>
++#include <stdlib.h>
++
++void __fatal(const char *file, int line, const char *fmt, ...)
++{
++ va_list args;
++ va_start(args, fmt);
++ vfprintf(stderr, fmt, args);
++ fprintf(stderr, " (%s:%u)\n", file, line);
++ fflush(stderr);
++ va_end(args);
++ exit(1);
++}
++
++void __fatalerror(const char *file, int line, const char *fmt, ...)
++{
++ va_list args;
++ va_start(args, fmt);
++ vfprintf(stderr, fmt, args);
++ fprintf(stderr, " (%s:%u): ", file, line);
++ perror(NULL);
++ fflush(stderr);
++ va_end(args);
++ exit(1);
++}
+diff --git a/src/libprocess/fatal.hpp b/src/libprocess/fatal.hpp
+new file mode 100644
+index 0000000..38646f3
+--- /dev/null
++++ b/src/libprocess/fatal.hpp
+@@ -0,0 +1,28 @@
++/*
++ * Basic perror + exit routines.
++ *
++ * Contributed by Benjamin Hindman <benh at berkeley.edu>, 2008.
++ */
++
++#ifndef FATAL_HPP
++#define FATAL_HPP
++
++#include <stdarg.h>
++#include <stdio.h>
++#include <stdlib.h>
++
++/*
++ * Like the non-debug version except includes the file name and line
++ * number in the output.
++ */
++#define fatal(fmt...) __fatal(__FILE__, __LINE__, fmt)
++void __fatal(const char *file, int line, const char *fmt, ...);
++
++/*
++ * Like the non-debug version except includes the file name and line
++ * number in the output.
++ */
++#define fatalerror(fmt...) __fatalerror(__FILE__, __LINE__, fmt)
++void __fatalerror(const char *file, int line, const char *fmt, ...);
++
++#endif /* FATAL_HPP */
+diff --git a/src/libprocess/gate.hpp b/src/libprocess/gate.hpp
+new file mode 100644
+index 0000000..954f620
+--- /dev/null
++++ b/src/libprocess/gate.hpp
+@@ -0,0 +1,103 @@
++#ifndef GATE_H
++#define GATE_H
++
++/* TODO(benh): Provide an implementation directly on-top-of futex's for Linux. */
++//#ifdef __linux__
++//#else
++
++class Gate
++{
++public:
++ typedef intptr_t state_t;
++
++private:
++ int waiters;
++ state_t state;
++ pthread_mutex_t mutex;
++ pthread_cond_t cond;
++
++public:
++ Gate() : waiters(0), state(0)
++ {
++ pthread_mutex_init(&mutex, NULL);
++ pthread_cond_init(&cond, NULL);
++ }
++
++ ~Gate()
++ {
++ pthread_cond_destroy(&cond);
++ pthread_mutex_destroy(&mutex);
++ }
++
++ void open(bool all = true)
++ {
++ pthread_mutex_lock(&mutex);
++ {
++ state++;
++ if (all) pthread_cond_broadcast(&cond);
++ else pthread_cond_signal(&cond);
++ }
++ pthread_mutex_unlock(&mutex);
++ }
++
++ void wait()
++ {
++ pthread_mutex_lock(&mutex);
++ {
++ waiters++;
++ state_t old = state;
++ while (old == state)
++ pthread_cond_wait(&cond, &mutex);
++ waiters--;
++ }
++ pthread_mutex_unlock(&mutex);
++ }
++
++ state_t approach()
++ {
++ state_t old;
++ pthread_mutex_lock(&mutex);
++ {
++ waiters++;
++ old = state;
++ }
++ pthread_mutex_unlock(&mutex);
++ return old;
++ }
++
++ void arrive(state_t old)
++ {
++ pthread_mutex_lock(&mutex);
++ {
++ while (old == state) {
++ pthread_cond_wait(&cond, &mutex);
++ }
++ waiters--;
++ }
++ pthread_mutex_unlock(&mutex);
++ }
++
++ void leave()
++ {
++ pthread_mutex_lock(&mutex);
++ {
++ waiters--;
++ }
++ pthread_mutex_unlock(&mutex);
++ }
++
++ bool empty()
++ {
++ bool occupied = true;
++ pthread_mutex_lock(&mutex);
++ {
++ occupied = waiters > 0 ? true : false;
++ }
++ pthread_mutex_unlock(&mutex);
++ return !occupied;
++ }
++};
++
++//#endif
++
++#endif /* GATE_H */
+diff --git a/src/libprocess/httpd.cpp b/src/libprocess/httpd.cpp
+new file mode 100644
+index 0000000..bbd5251
+--- /dev/null
++++ b/src/libprocess/httpd.cpp
+@@ -0,0 +1,306 @@
++/* TODO(benh): TCP_CORK!!!!! */
++/* TODO(benh): Complete HttpParser & HttpMessage implementation. */
++/* TODO(benh): Turn off Nagle (on TCP_NODELAY) for pipelined requests. */
++
++#include <string.h>
++
++#include <process.hpp>
++#include <tuple.hpp>
++
++#include <iostream>
++#include <map>
++#include <sstream>
++#include <stdexcept>
++
++#include <arpa/inet.h>
++
++#include <stout/os.hpp>
++
++#include "net.hpp"
++
++#include "http-parser/http_parser.h"
++
++using std::cerr;
++using std::cout;
++using std::endl;
++using std::runtime_error;
++using std::string;
++using std::map;
++
++using process::tuple::Tuple;
++
++#define malloc(bytes) \
++ ({ void *tmp; if ((tmp = malloc(bytes)) == NULL) abort(); tmp; })
++
++#define realloc(address, bytes) \
++ ({ void *tmp; if ((tmp = realloc(address, bytes)) == NULL) abort(); tmp; })
++
++#define HTTP_500 \
++ "HTTP/1.1 500 Internal Server Error\r\n\r\n"
++
++#define HTTP_501 \
++ "HTTP/1.1 501 Not Implemented\r\n\r\n"
++
++#define HTTP_404 \
++ "HTTP/1.1 404 Not Found\r\n\r\n"
++
++
++struct HttpMessage
++{
++ unsigned short method;
++ /* TODO(*): Use HTTP_MAX_URI_SIZE. */
++ string uri;
++};
++
++
++class HttpParser
++{
++protected:
++ static int on_uri(http_parser *parser, const char *p, size_t len)
++ {
++ HttpMessage *message = (HttpMessage *) parser->data;
++ message->uri += string(p, len);
++ return 0;
++ }
++
++ static int on_headers_complete(http_parser *parser)
++ {
++ HttpMessage *message = (HttpMessage *) parser->data;
++ message->method = parser->method;
++ return 0;
++ }
++
++public:
++ static HttpMessage * parse(const string &raw)
++ {
++ http_parser parser;
++ http_parser_init(&parser, HTTP_REQUEST);
++
++ HttpMessage *message = new HttpMessage;
++
++ parser.data = message;
++
++ parser.on_message_begin = NULL;
++ parser.on_header_field = NULL;
++ parser.on_header_value = NULL;
++ parser.on_path = NULL;
++ parser.on_uri = &HttpParser::on_uri;
++ parser.on_fragment = NULL;
++ parser.on_query_string = NULL;
++ parser.on_body = NULL;
++ parser.on_headers_complete = &HttpParser::on_headers_complete;
++ parser.on_message_complete = NULL;
++
++ http_parser_execute(&parser, raw.c_str(), raw.length());
++
++ if (http_parser_has_error(&parser)) {
++ //cerr << "parser error" << endl;
++ abort();
++ }
++
++ return message;
++ }
++};
++
++
++class HttpConnection : public SocketProcess<TCP>
++{
++protected:
++ void operator () ()
++ {
++ //cout << ht_id() << ": running " << this << " connection (1)" << endl;
++
++ string raw;
++
++ /* Read headers (until CRLF CRLF). */
++ do {
++ char buf[512];
++ ssize_t len = recv(buf, 512);
++ raw += string(buf, len);
++ } while (raw.find("\r\n\r\n") == string::npos);
++
++ //cout << ht_id() << ": running " << this << " connection (2)" << endl;
++
++ /* Parse headers. */
++ HttpMessage *message = HttpParser::parse(raw);
++
++ /* Handle request. */
++ switch (message->method) {
++ case HTTP_GET: {
++ message->uri =
++ message->uri != "/"
++ ? "." + message->uri
++ : "./index.html";
++
++ //cout << "URI: " << message->uri << endl;
++
++ /* Open file (if possible). */
++ int fd;
++
++ if ((fd = open(message->uri.c_str(), O_RDONLY, 0)) < 0) {
++ send(HTTP_404, strlen(HTTP_404));
++ return;
++ }
++
++ /* Lookup file size. */
++ struct stat fd_stat;
++
++ if (fstat(fd, &fd_stat) < 0) {
++ send(HTTP_500, strlen(HTTP_500));
++ os::close(fd);
++ return;
++ }
++
++ /* TODO(benh): Use TCP_CORK. */
++
++ /* Transmit reply header. */
++ std::stringstream out;
++
++ out << "HTTP/1.1 200 OK\r\n";
++
++ /* Determine the content type. */
++ if (message->uri.find(".jpg") != string::npos) {
++ out << "Content-Type: image/jpeg\r\n";
++ } else if (message->uri.find(".gif") != string::npos) {
++ out << "Content-Type: image/gif\r\n";
++ } else if (message->uri.find(".png") != string::npos) {
++ out << "Content-Type: image/png\r\n";
++ } else if (message->uri.find(".css") != string::npos) {
++ out << "Content-Type: text/css\r\n";
++ } else {
++ out << "Content-Type: text/html\r\n";
++ }
++
++ out <<
++ "Content-Length: " << fd_stat.st_size << "\r\n"
++ "\r\n";
++
++ //cout << out.str() << endl;
++
++ send(out.str().c_str(), out.str().size());
++
++ //cout << ht_id() << ": running " << this << " connection (3)" << endl;
++
++ /* Transmit file (TODO(benh): Use file cache.). */
++ sendfile(fd, fd_stat.st_size);
++
++ //cout << ht_id() << ": running " << this << " connection (4)" << endl;
++
++ os::close(fd);
++
++ break;
++ }
++
++ default:
++ /* Unimplemented. */
++ send(HTTP_501, strlen(HTTP_501));
++ break;
++ }
++ }
++
++public:
++ HttpConnection(int s) : SocketProcess<TCP>(s) {}
++ ~HttpConnection() {}
++};
++
++
++enum HTTP_MESSAGES { ACCEPT = PROCESS_MSGID };
++
++namespace process { namespace tuple { TUPLE(::ACCEPT, (int)); }}
++
++class HttpAcceptor : public Tuple< Acceptor<TCP> >
++{
++private:
++ PID server;
++
++protected:
++ void operator () ()
++ {
++ do {
++ struct sockaddr_in addr;
++ int c = accept(addr);
++ //cout << ht_id() << ": running acceptor" << endl;
++ send<ACCEPT>(server, c);
++ } while (true);
++ }
++
++public:
++ HttpAcceptor(const PID &_server, int s) : server(_server) { socket(s); }
++};
++
++
++
++class HttpServer : public Tuple< Server<TCP> >
++{
++private:
++ map<PID, HttpConnection *> connections;
++
++protected:
++ void operator () ()
++ {
++ int on = 1;
++ setsockopt(SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
++ bind();
++ listen(100000);
++
++ HttpAcceptor *acceptor = new HttpAcceptor(self(), s);
++ link(spawn(acceptor));
++
++ do {
++ switch (receive()) {
++ case ACCEPT: {
++ //cout << ht_id() << ": running server (accept)" << endl;
++ int c;
++ unpack<ACCEPT>(c);
++ HttpConnection *connection = new HttpConnection(c);
++ connections[link(spawn(connection))] = connection;
++ //cout << "...started (" << connection << ")..." << endl;
++ break;
++ }
++ case PROCESS_EXIT: {
++ //cout << ht_id() << ": running server (exit)" << endl;
++ if (from() == acceptor->getPID()) {
++ throw runtime_error("unimplemented acceptor failure");
++ } else if (connections.find(from()) != connections.end()) {
++ HttpConnection *connection = connections[from()];
++ connections.erase(from());
++ delete connection;
++ //cout << "...finished (" << connection << ")..." << endl;
++ }
++ break;
++ }
++ default:
++ cout << "HttpServer received unexpected message" << endl;
++ break;
++ }
++ } while (true);
++ }
++
++public:
++ HttpServer(int port) { init(INADDR_ANY, port); }
++};
++
++
++
++int main(int argc, char **argv)
++{
++ /* TODO(benh): Blah, 'sendfile' doesn't let us use MSG_NOSIGNAL. :( */
++ signal(SIGPIPE, SIG_IGN);
++
++ if (argc != 2) {
++ cerr << "usage: " << argv[0] << " <port>" << endl;
++ return -1;
++ }
++
++#ifdef USE_LITHE
++ ProcessScheduler *scheduler = new ProcessScheduler();
++ Process::spawn(new HttpServer(atoi(argv[1])));
++ /* TODO(benh): Make Process::wait take and use the hart if using Lithe! */
++ for (;;)
++ sleep(10000);
++#else
++ Process::wait(Process::spawn(new HttpServer(atoi(argv[1]))));
++#endif /* USE_LITHE */
++
++ return 0;
++}
+diff --git a/src/libprocess/latch.cpp b/src/libprocess/latch.cpp
+new file mode 100644
+index 0000000..a6f1256
+--- /dev/null
++++ b/src/libprocess/latch.cpp
+@@ -0,0 +1,62 @@
++#include <process/id.hpp>
++#include <process/latch.hpp>
++#include <process/process.hpp>
++
++#include <stout/duration.hpp>
++
++namespace process {
++
++// TODO(benh): Provide an "optimized" implementation of a latch that
++// is libprocess aware. That is, allow integrate "waiting" on a latch
++// within libprocess such that it doesn't cost a memory allocation, a
++// spawn, a message send, a wait, and two user-space context-switchs.
++
++Latch::Latch()
++{
++ triggered = false;
++
++ // Deadlock is possible if one thread is trying to delete a latch
++ // but the libprocess thread(s) is trying to acquire a resource the
++ // deleting thread is holding. Hence, we only save the PID for
++ // triggering the latch and let the GC actually do the deleting
++ // (thus no waiting is necessary, and deadlocks are avoided).
++ pid = spawn(new ProcessBase(ID::generate("__latch__")), true);
++}
++
++
++Latch::~Latch()
++{
++ terminate(pid);
++}
++
++
++void Latch::trigger()
++{
++ if (!triggered) {
++ terminate(pid);
++ triggered = true;
++ }
++}
++
++
++bool Latch::await(const Duration& duration)
++{
++ if (!triggered) {
++ process::wait(pid, duration); // Explict to disambiguate.
++ // It's possible that we failed to wait because:
++ // (1) Our process has already terminated.
++ // (2) We timed out (i.e., duration was not "infinite").
++
++ // In the event of (1) we might need to return 'true' since a
++ // terminated process might imply that the latch has been
++ // triggered. To capture this we simply return the value of
++ // 'triggered' (which will also capture cases where we actually
++ // timed out but have since triggered, which seems like an
++ // acceptable semantics given such a "tie").
++ return triggered;
++ }
++
++ return true;
++}
++
++} // namespace process {
+diff --git a/src/libprocess/net.hpp b/src/libprocess/net.hpp
+new file mode 100644
+index 0000000..2fdc62a
+--- /dev/null
++++ b/src/libprocess/net.hpp
+@@ -0,0 +1,231 @@
++/* TODO(benh): Write a form of 'Client' process. */
++
++#ifndef NET_HPP
++#define NET_HPP
++
++#include <assert.h>
++#include <errno.h>
++#include <fcntl.h>
++
++#include <process.hpp>
++
++#include <netinet/in.h>
++#include <netinet/tcp.h>
++#include <netinet/udp.h>
++
++#include <sys/ioctl.h>
++#include <sys/sendfile.h>
++#include <sys/socket.h>
++
++#include <stdexcept>
++#include <iostream>
++
++#include <stout/os.hpp>
++
++typedef enum Protocol { TCP = SOCK_STREAM, UDP = SOCK_DGRAM } Protocol;
++
++using std::runtime_error;
++using std::string;
++
++
++template <Protocol protocol>
++class SocketProcess : public Process
++{
++protected:
++ int s;
++
++ void setsockopt(int level, int optname, const void *optval, socklen_t optlen)
++ {
++ if (::setsockopt(s, level, optname, optval, optlen) < 0)
++ throw std::runtime_error(string("setsockopt: ") += strerror(errno));
++ }
++
++ virtual void socket()
++ {
++ if ((s = ::socket(AF_INET, protocol, IPPROTO_IP)) < 0)
++ throw runtime_error(string("socket: ") += strerror(errno));
++
++ socket(s);
++ }
++
++ virtual void socket(int sd)
++ {
++ s = sd;
++
++ int flags = 1;
++ if (ioctl(s, FIONBIO, &flags) &&
++ ((flags = fcntl(s, F_GETFL, 0)) < 0 ||
++ fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0))
++ throw runtime_error(string("ioctl/fcntl: ") += strerror(errno));
++
++ if (fcntl(s, F_SETFD, FD_CLOEXEC) < 0) {
++ throw runtime_error(string("fcntl: ") += strerror(errno));
++ }
++ }
++
++ virtual void bind(in_addr_t ip, in_port_t port)
++ {
++ struct sockaddr_in addr;
++ addr.sin_family = PF_INET;
++ addr.sin_addr.s_addr = ip;
++ addr.sin_port = htons(port);
++
++ if (::bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0)
++ throw runtime_error(string("bind: ") += strerror(errno));
++ }
++
++ virtual ssize_t recv(void *buf, size_t bytes)
++ {
++ ssize_t len = 0;
++ do {
++ len = ::recv(s, buf, bytes, 0);
++
++ if (len > 0)
++ return len;
++ else if (len < 0 && errno == EWOULDBLOCK)
++ while (!await(s, RDONLY));
++ else if (len == 0)
++ throw runtime_error(string("recv: connection terminated"));
++ else
++ throw runtime_error(string("recv: ") += strerror(errno));
++ } while (!(len > 0));
++
++ return len;
++ }
++
++ virtual ssize_t recvall(void *buf, size_t bytes)
++ {
++ ssize_t len, offset = 0;
++ do {
++ len = ::recv(s, (char *) buf + offset, bytes - offset, 0);
++
++ if (len > 0)
++ offset += len;
++ else if (len < 0 && errno == EWOULDBLOCK)
++ while (!await(s, RDONLY));
++ else if (len == 0)
++ throw runtime_error(string("recvall: connection terminated"));
++ else
++ throw runtime_error(string("recvall: ") += strerror(errno));
++ } while (offset != bytes);
++
++ return offset;
++ }
++
++ virtual void send(const void *buf, size_t bytes)
++ {
++ size_t offset = 0;
++ do {
++ size_t len =
++ ::send(s, (char *) buf + offset, bytes - offset, MSG_NOSIGNAL);
++
++ if (len > 0)
++ offset += len;
++ else if (len < 0 && errno == EWOULDBLOCK)
++ while (!await(s, WRONLY));
++ else if (len == 0)
++ throw runtime_error(string("send: connection terminated"));
++ else
++ throw runtime_error(string("send: ") += strerror(errno));
++ } while (offset != bytes);
++ }
++
++ virtual void sendfile(int fd, size_t bytes)
++ {
++ off_t offset = 0;
++ do {
++ size_t len = ::sendfile(s, fd, 0, bytes - offset);
++
++ if (len > 0)
++ offset += len;
++ else if (len < 0 && errno == EWOULDBLOCK)
++ while (!await(s, WRONLY));
++ else if (len == 0)
++ throw runtime_error(string("sendfile: connection terminated"));
++ else
++ throw runtime_error(string("sendfile: ") += strerror(errno));
++ } while (offset != bytes);
++ }
++
++public:
++ SocketProcess() : s(-1) {}
++ SocketProcess(int _s) : s(_s)
++ {
++ int flags = 1;
++ if (ioctl(s, FIONBIO, &flags) &&
++ ((flags = fcntl(s, F_GETFL, 0)) < 0 ||
++ fcntl(s, F_SETFL, flags | O_NONBLOCK) < 0))
++ throw runtime_error(string("ioctl/fcntl: ") += strerror(errno));
++ }
++ ~SocketProcess() { os::close(s); }
++};
++
++
++template <Protocol protocol>
++class Acceptor : public SocketProcess<protocol>
++{
++protected:
++ virtual int accept(struct sockaddr_in &addr)
++ {
++ int c;
++
++ do {
++ while (!await(SocketProcess<protocol>::s, Process::RDONLY));
++
++ size_t size = sizeof(struct sockaddr_in);
++
++ c = ::accept(SocketProcess<protocol>::s,
++ (struct sockaddr *) &addr,
++ (socklen_t *) &size);
++
++ if (c == 0)
++ throw runtime_error(string("accept: ") += strerror(errno));
++ else if (c < 0 && (errno != EWOULDBLOCK))
++ throw runtime_error(string("accept: ") += strerror(errno));
++ } while (!(c > 0));
++
++ return c;
++ }
++
++public:
++ Acceptor() {}
++ Acceptor(int s) : SocketProcess<protocol>(s) {}
++};
++
++
++template <Protocol protocol>
++class Server : public Acceptor<protocol>
++{
++protected:
++ in_addr_t ip;
++ in_port_t port;
++
++ void init(in_addr_t _ip = INADDR_ANY, in_port_t _port = 0)
++ {
++ ip = _ip;
++ port = _port;
++ SocketProcess<protocol>::socket();
++ }
++
++ virtual void listen(int n)
++ {
++ int &s = SocketProcess<protocol>::s;
++ if (::listen(s, n) < 0)
++ throw runtime_error(string("listen: ") += strerror(errno));
++ }
++
++ virtual void bind()
++ {
++ SocketProcess<protocol>::bind(ip, port);
++ }
++
++public:
++ Server(in_addr_t _ip = INADDR_ANY, in_port_t _port = 0)
++ : ip(_ip), port(_port)
++ {
++ SocketProcess<protocol>::socket();
++ }
++};
++
++
++#endif /* NET_HH */
+diff --git a/src/libprocess/pid.cpp b/src/libprocess/pid.cpp
+new file mode 100644
+index 0000000..becc46b
+--- /dev/null
++++ b/src/libprocess/pid.cpp
+@@ -0,0 +1,179 @@
++#include <errno.h>
++#include <netdb.h>
++#include <stdio.h>
++#include <string.h>
++
++#include <arpa/inet.h>
++
++#include <glog/logging.h>
++
++#include <iostream>
++#include <string>
++
++#include <boost/unordered_map.hpp>
++
++#include <process/pid.hpp>
++#include <process/process.hpp>
++
++#include <stout/os.hpp>
++
++#include "config.hpp"
++
++
++using std::istream;
++using std::ostream;
++using std::size_t;
++using std::string;
++
++
++namespace process {
++
++UPID::UPID(const char* s)
++{
++ std::istringstream in(s);
++ in >> *this;
++}
++
++
++UPID::UPID(const std::string& s)
++{
++ std::istringstream in(s);
++ in >> *this;
++}
++
++
++// TODO(benh): Make this inline-able (cyclic dependency issues).
++UPID::UPID(const ProcessBase& process)
++{
++ id = process.self().id;
++ ip = process.self().ip;
++ port = process.self().port;
++}
++
++
++UPID::operator std::string() const
++{
++ std::ostringstream out;
++ out << *this;
++ return out.str();
++}
++
++
++ostream& operator << (ostream& stream, const UPID& pid)
++{
++ // Call inet_ntop since inet_ntoa is not thread-safe!
++ char ip[INET_ADDRSTRLEN];
++ if (inet_ntop(AF_INET, (in_addr *) &pid.ip, ip, INET_ADDRSTRLEN) == NULL)
++ memset(ip, 0, INET_ADDRSTRLEN);
++
++ stream << pid.id << "@" << ip << ":" << pid.port;
++ return stream;
++}
++
++
++istream& operator >> (istream& stream, UPID& pid)
++{
++ pid.id = "";
++ pid.ip = 0;
++ pid.port = 0;
++
++ string str;
++ if (!(stream >> str)) {
++ stream.setstate(std::ios_base::badbit);
++ return stream;
++ }
++
++ VLOG(2) << "Attempting to parse '" << str << "' into a PID";
++
++ if (str.size() == 0) {
++ stream.setstate(std::ios_base::badbit);
++ return stream;
++ }
++
++ string id;
++ string host;
++ uint32_t ip;
++ uint16_t port;
++
++ size_t index = str.find('@');
++
++ if (index != string::npos) {
++ id = str.substr(0, index);
++ } else {
++ stream.setstate(std::ios_base::badbit);
++ return stream;
++ }
++
++ str = str.substr(index + 1);
++
++ index = str.find(':');
++
++ if (index != string::npos) {
++ host = str.substr(0, index);
++ } else {
++ stream.setstate(std::ios_base::badbit);
++ return stream;
++ }
++
++ hostent he, *hep;
++ char* temp;
++ size_t length;
++ int result;
++ int herrno;
++
++ // Allocate temporary buffer for gethostbyname2_r.
++ length = 1024;
++ temp = new char[length];
++
++ while ((result = gethostbyname2_r(
++ host.c_str(), AF_INET, &he, temp, length, &hep, &herrno)) == ERANGE) {
++ // Enlarge the buffer.
++ delete[] temp;
++ length *= 2;
++ temp = new char[length];
++ }
++
++ if (result != 0 || hep == NULL) {
++ VLOG(2) << "Failed to parse host '" << host
++ << "' because " << hstrerror(herrno);
++ delete[] temp;
++ stream.setstate(std::ios_base::badbit);
++ return stream;
++ }
++
++ if (hep->h_addr_list[0] == NULL) {
++ VLOG(2) << "Got no addresses for '" << host << "'";
++ delete[] temp;
++ stream.setstate(std::ios_base::badbit);
++ return stream;
++ }
++
++ ip = *((uint32_t*) hep->h_addr_list[0]);
++
++ delete[] temp;
++
++ str = str.substr(index + 1);
++
++ if (sscanf(str.c_str(), "%hu", &port) != 1) {
++ stream.setstate(std::ios_base::badbit);
++ return stream;
++ }
++
++ pid.id = id;
++ pid.ip = ip;
++ pid.port = port;
++
++ return stream;
++}
++
++
++size_t hash_value(const UPID& pid)
++{
++ size_t seed = 0;
++ boost::hash_combine(seed, pid.id);
++ boost::hash_combine(seed, pid.ip);
++ boost::hash_combine(seed, pid.port);
++ return seed;
++}
++
++} // namespace process {
+diff --git a/src/libprocess/process.cpp b/src/libprocess/process.cpp
+new file mode 100644
+index 0000000..2d193b1
+--- /dev/null
++++ b/src/libprocess/process.cpp
+@@ -0,0 +1,3708 @@
++#include <errno.h>
++#include <ev.h>
++#include <limits.h>
++#include <libgen.h>
++#include <netdb.h>
++#include <pthread.h>
++#include <signal.h>
++#include <stdarg.h>
++#include <stdio.h>
++#include <stdlib.h>
++#include <string.h>
++#include <unistd.h>
++
++#include <arpa/inet.h>
++
++#include <glog/logging.h>
++
++#include <netinet/in.h>
++#include <netinet/tcp.h>
++
++#include <sys/ioctl.h>
++#include <sys/mman.h>
++#include <sys/select.h>
++#include <sys/socket.h>
++#include <sys/time.h>
++#include <sys/types.h>
++#include <sys/uio.h>
++
++#include <algorithm>
++#include <deque>
++#include <fstream>
++#include <iomanip>
++#include <iostream>
++#include <list>
++#include <map>
++#include <queue>
++#include <set>
++#include <sstream>
++#include <stack>
++#include <stdexcept>
++#include <vector>
++
++#include <tr1/functional>
++#include <tr1/memory> // TODO(benh): Replace all shared_ptr with unique_ptr.
++
++#include <boost/shared_array.hpp>
++
++#include <process/clock.hpp>
++#include <process/defer.hpp>
++#include <process/delay.hpp>
++#include <process/dispatch.hpp>
++#include <process/executor.hpp>
++#include <process/filter.hpp>
++#include <process/future.hpp>
++#include <process/gc.hpp>
++#include <process/help.hpp>
++#include <process/id.hpp>
++#include <process/io.hpp>
++#include <process/logging.hpp>
++#include <process/mime.hpp>
++#include <process/process.hpp>
++#include <process/profiler.hpp>
++#include <process/socket.hpp>
++#include <process/statistics.hpp>
++#include <process/time.hpp>
++#include <process/timer.hpp>
++
++#include <stout/duration.hpp>
++#include <stout/foreach.hpp>
++#include <stout/lambda.hpp>
++#include <stout/net.hpp>
++#include <stout/os.hpp>
++#include <stout/strings.hpp>
++#include <stout/thread.hpp>
++
++#include "config.hpp"
++#include "decoder.hpp"
++#include "encoder.hpp"
++#include "gate.hpp"
++#include "synchronized.hpp"
++
++using process::wait; // Necessary on some OS's to disambiguate.
++
++using process::http::BadRequest;
++using process::http::InternalServerError;
++using process::http::NotFound;
++using process::http::OK;
++using process::http::Request;
++using process::http::Response;
++using process::http::ServiceUnavailable;
++
++using std::deque;
++using std::find;
++using std::list;
++using std::map;
++using std::ostream;
++using std::pair;
++using std::queue;
++using std::set;
++using std::stack;
++using std::string;
++using std::stringstream;
++using std::vector;
++
++// Represents a remote "node" (encapsulates IP address and port).
++class Node
++{
++public:
++ Node(uint32_t _ip = 0, uint16_t _port = 0)
++ : ip(_ip), port(_port) {}
++
++ bool operator < (const Node& that) const
++ {
++ if (ip == that.ip) {
++ return port < that.port;
++ } else {
++ return ip < that.ip;
++ }
++ }
++
++ ostream& operator << (ostream& stream) const
++ {
++ stream << ip << ":" << port;
++ return stream;
++ }
++
++ uint32_t ip;
++ uint16_t port;
++};
++
++
++namespace process {
++
++namespace ID {
++
++string generate(const string& prefix)
++{
++ static map<string, int> prefixes;
++ static synchronizable(prefixes) = SYNCHRONIZED_INITIALIZER;
++
++ int id;
++ synchronized (prefixes) {
++ int& _id = prefixes[prefix];
++ _id += 1;
++ id = _id;
++ }
++ return prefix + "(" + stringify(id) + ")";
++}
++
++} // namespace ID {
++
++
++namespace http {
++
++hashmap<uint16_t, string> statuses;
++
++} // namespace http {
++
++
++namespace mime {
++
++map<string, string> types;
++
++} // namespace mime {
++
++
++// Provides reference counting semantics for a process pointer.
++class ProcessReference
++{
++public:
++ ProcessReference() : process(NULL) {}
++
++ ~ProcessReference()
++ {
++ cleanup();
++ }
++
++ ProcessReference(const ProcessReference& that)
++ {
++ copy(that);
++ }
++
++ ProcessReference& operator = (const ProcessReference& that)
++ {
++ if (this != &that) {
++ cleanup();
++ copy(that);
++ }
++ return *this;
++ }
++
++ ProcessBase* operator -> ()
++ {
++ return process;
++ }
++
++ operator ProcessBase* ()
++ {
++ return process;
++ }
++
++ operator bool () const
++ {
++ return process != NULL;
++ }
++
++private:
++ friend class ProcessManager; // For ProcessManager::use.
++
++ ProcessReference(ProcessBase* _process)
++ : process(_process)
++ {
++ if (process != NULL) {
++ __sync_fetch_and_add(&(process->refs), 1);
++ }
++ }
++
++ void copy(const ProcessReference& that)
++ {
++ process = that.process;
++
++ if (process != NULL) {
++ // There should be at least one reference to the process, so
++ // we don't need to worry about checking if it's exiting or
++ // not, since we know we can always create another reference.
++ CHECK(process->refs > 0);
++ __sync_fetch_and_add(&(process->refs), 1);
++ }
++ }
++
++ void cleanup()
++ {
++ if (process != NULL) {
++ __sync_fetch_and_sub(&(process->refs), 1);
++ }
++ }
++
++ ProcessBase* process;
++};
++
++
++// Provides a process that manages sending HTTP responses so as to
++// satisfy HTTP/1.1 pipelining. Each request should either enqueue a
++// response, or ask the proxy to handle a future response. The process
++// is responsible for making sure the responses are sent in the same
++// order as the requests. Note that we use a 'Socket' in order to keep
++// the underyling file descriptor from getting closed while there
++// might still be outstanding responses even though the client might
++// have closed the connection (see more discussion in
++// SocketManger::close and SocketManager::proxy).
++class HttpProxy : public Process<HttpProxy>
++{
++public:
++ HttpProxy(const Socket& _socket);
++ virtual ~HttpProxy();
++
++ // Enqueues the response to be sent once all previously enqueued
++ // responses have been processed (e.g., waited for and sent).
++ void enqueue(const Response& response, const Request& request);
++
++ // Enqueues a future to a response that will get waited on (up to
++ // some timeout) and then sent once all previously enqueued
++ // responses have been processed (e.g., waited for and sent).
++ void handle(Future<Response>* future, const Request& request);
++
++private:
++ // Starts "waiting" on the next available future response.
++ void next();
++
++ // Invoked once a future response has been satisfied.
++ void waited(const Future<Response>& future);
++
++ // Demuxes and handles a response.
++ bool process(const Future<Response>& future, const Request& request);
++
++ // Handles stream (i.e., pipe) based responses.
++ void stream(const Future<short>& poll, const Request& request);
++
++ Socket socket; // Wrap the socket to keep it from getting closed.
++
++ // Describes a queue "item" that wraps the future to the response
++ // and the original request.
++ // The original request contains needed information such as what encodings
++ // are acceptable and whether to persist the connection.
++ struct Item
++ {
++ Item(const Request& _request, Future<Response>* _future)
++ : request(_request), future(_future) {}
++
++ ~Item()
++ {
++ delete future;
++ }
++
++ const Request request; // Make a copy.
++ Future<Response>* future;
++ };
++
++ queue<Item*> items;
++
++ Option<int> pipe; // Current pipe, if streaming.
++};
++
++
++// Helper for creating routes without a process.
++// TODO(benh): Move this into route.hpp.
++class Route
++{
++public:
++ Route(const string& name,
++ const Option<string>& help,
++ const lambda::function<Future<Response>(const Request&)>& handler)
++ {
++ process = new RouteProcess(name, help, handler);
++ spawn(process);
++ }
++
++ ~Route()
++ {
++ terminate(process);
++ wait(process);
++ }
++
++private:
++ class RouteProcess : public Process<RouteProcess>
++ {
++ public:
++ RouteProcess(
++ const string& name,
++ const Option<string>& _help,
++ const lambda::function<Future<Response>(const Request&)>& _handler)
++ : ProcessBase(strings::remove(name, "/", strings::PREFIX)),
++ help(_help),
++ handler(_handler) {}
++
++ protected:
++ virtual void initialize()
++ {
++ route("/", help, &RouteProcess::handle);
++ }
++
++ Future<Response> handle(const Request& request)
++ {
++ return handler(request);
++ }
++
++ const Option<string> help;
++ const lambda::function<Future<Response>(const Request&)> handler;
++ };
++
++ RouteProcess* process;
++};
++
++
++class SocketManager
++{
++public:
++ SocketManager();
++ ~SocketManager();
++
++ Socket accepted(int s);
++
++ void link(ProcessBase* process, const UPID& to);
++
++ PID<HttpProxy> proxy(const Socket& socket);
++
++ void send(Encoder* encoder, bool persist);
++ void send(const Response& response,
++ const Request& request,
++ const Socket& socket);
++ void send(Message* message);
++
++ Encoder* next(int s);
++
++ void close(int s);
++
++ void exited(const Node& node);
++ void exited(ProcessBase* process);
++
++private:
++ // Map from UPID (local/remote) to process.
++ map<UPID, set<ProcessBase*> > links;
++
++ // Collection of all actice sockets.
++ map<int, Socket> sockets;
++
++ // Collection of sockets that should be disposed when they are
++ // finished being used (e.g., when there is no more data to send on
++ // them).
++ set<int> dispose;
++
++ // Map from socket to node (ip, port).
++ map<int, Node> nodes;
++
++ // Maps from node (ip, port) to temporary sockets (i.e., they will
++ // get closed once there is no more data to send on them).
++ map<Node, int> temps;
++
++ // Maps from node (ip, port) to persistent sockets (i.e., they will
++ // remain open even if there is no more data to send on them). We
++ // distinguish these from the 'temps' collection so we can tell when
++ // a persistant socket has been lost (and thus generate
++ // ExitedEvents).
++ map<Node, int> persists;
++
++ // Map from socket to outgoing queue.
++ map<int, queue<Encoder*> > outgoing;
++
++ // HTTP proxies.
++ map<int, HttpProxy*> proxies;
++
++ // Protects instance variables.
++ synchronizable(this);
++};
++
++
++class ProcessManager
++{
++public:
++ ProcessManager(const string& delegate);
++ ~ProcessManager();
++
++ ProcessReference use(const UPID& pid);
++
++ bool handle(
++ const Socket& socket,
++ Request* request);
++
++ bool deliver(
++ ProcessBase* receiver,
++ Event* event,
++ ProcessBase* sender = NULL);
++
++ bool deliver(
++ const UPID& to,
++ Event* event,
++ ProcessBase* sender = NULL);
++
++ UPID spawn(ProcessBase* process, bool manage);
++ void resume(ProcessBase* process);
++ void cleanup(ProcessBase* process);
++ void link(ProcessBase* process, const UPID& to);
++ void terminate(const UPID& pid, bool inject, ProcessBase* sender = NULL);
++ bool wait(const UPID& pid);
++
++ void enqueue(ProcessBase* process);
++ ProcessBase* dequeue();
++
++ void settle();
++
++ // The /__processes__ route.
++ Future<Response> __processes__(const Request&);
++
++private:
++ // Delegate process name to receive root HTTP requests.
++ const string delegate;
++
++ // Map of all local spawned and running processes.
++ map<string, ProcessBase*> processes;
++ synchronizable(processes);
++
++ // Gates for waiting threads (protected by synchronizable(processes)).
++ map<ProcessBase*, Gate*> gates;
++
++ // Queue of runnable processes (implemented using list).
++ list<ProcessBase*> runq;
++ synchronizable(runq);
++
++ // Number of running processes, to support Clock::settle operation.
++ int running;
++};
++
++
++// Help strings.
++const string Logging::TOGGLE_HELP = HELP(
++ TLDR(
++ "Sets the logging verbosity level for a specified duration."),
++ USAGE(
++ "/logging/toggle?level=VALUE&duration=VALUE"),
++ DESCRIPTION(
++ "The libprocess library uses [glog][glog] for logging. The library",
++ "only uses verbose logging which means nothing will be output unless",
++ "the verbosity level is set (by default it's 0, libprocess uses"
++ "levels 1, 2, and 3).",
++ "",
++ "**NOTE:** If your application uses glog this will also affect",
++ "your verbose logging.",
++ "",
++ "Required query parameters:",
++ "",
++ "> level=VALUE Verbosity level (e.g., 1, 2, 3)",
++ "> duration=VALUE Duration to keep verbosity level",
++ "> toggled (e.g., 10secs, 15mins, etc.)"),
++ REFERENCES(
++ "[glog]: https://code.google.com/p/google-glog"));
++
++
++const string Profiler::START_HELP = HELP(
++ TLDR(
++ "Starts profiling ..."),
++ USAGE(
++ "/profiler/start..."),
++ DESCRIPTION(
++ "...",
++ "",
++ "Query parameters:",
++ "",
++ "> param=VALUE Some description here"));
++
++
++const string Profiler::STOP_HELP = HELP(
++ TLDR(
++ "Stops profiling ..."),
++ USAGE(
++ "/profiler/stop..."),
++ DESCRIPTION(
++ "...",
++ "",
++ "Query parameters:",
++ "",
++ "> param=VALUE Some description here"));
++
++
++// Unique id that can be assigned to each process.
++static uint32_t __id__ = 0;
++
++// Local server socket.
++static int __s__ = -1;
++
++// Local IP address.
++static uint32_t __ip__ = 0;
++
++// Local port.
++static uint16_t __port__ = 0;
++
++// Active SocketManager (eventually will probably be thread-local).
++static SocketManager* socket_manager = NULL;
++
++// Active ProcessManager (eventually will probably be thread-local).
++static ProcessManager* process_manager = NULL;
++
++// Event loop.
++static struct ev_loop* loop = NULL;
++
++// Asynchronous watcher for interrupting loop.
++static ev_async async_watcher;
++
++// Watcher for timeouts.
++static ev_timer timeouts_watcher;
++
++// Server watcher for accepting connections.
++static ev_io server_watcher;
++
++// Queue of I/O watchers.
++static queue<ev_io*>* watchers = new queue<ev_io*>();
++static synchronizable(watchers) = SYNCHRONIZED_INITIALIZER;
++
++// We store the timers in a map of lists indexed by the timeout of the
++// timer so that we can have two timers that have the same timeout. We
++// exploit that the map is SORTED!
++static map<Time, list<Timer> >* timeouts =
++ new map<Time, list<Timer> >();
++static synchronizable(timeouts) = SYNCHRONIZED_INITIALIZER_RECURSIVE;
++
++// For supporting Clock::settle(), true if timers have been removed
++// from 'timeouts' but may not have been executed yet. Protected by
++// the timeouts lock. This is only used when the clock is paused.
++static bool pending_timers = false;
++
++// Flag to indicate whether or to update the timer on async interrupt.
++static bool update_timer = false;
++
++// Scheduling gate that threads wait at when there is nothing to run.
++static Gate* gate = new Gate();
++
++// Filter. Synchronized support for using the filterer needs to be
++// recursive incase a filterer wants to do anything fancy (which is
++// possible and likely given that filters will get used for testing).
++static Filter* filterer = NULL;
++static synchronizable(filterer) = SYNCHRONIZED_INITIALIZER_RECURSIVE;
++
++// Global garbage collector.
++PID<GarbageCollector> gc;
++
++// Global help.
++PID<Help> help;
++
++// Per thread process pointer.
++ThreadLocal<ProcessBase>* _process_ = new ThreadLocal<ProcessBase>();
++
++// Per thread executor pointer.
++ThreadLocal<Executor>* _executor_ = new ThreadLocal<Executor>();
++
++const Duration LIBPROCESS_STATISTICS_WINDOW = Days(1);
++
++// We namespace the clock related variables to keep them well
++// named. In the future we'll probably want to associate a clock with
++// a specific ProcessManager/SocketManager instance pair, so this will
++// likely change.
++namespace clock {
++
++map<ProcessBase*, Time>* currents = new map<ProcessBase*, Time>();
++
++Time initial = Time::EPOCH;
++Time current = Time::EPOCH;
++
++bool paused = false;
++
++} // namespace clock {
++
++
++Time Time::EPOCH = Time(Duration::zero());
++
++
++Time Time::MAX = Time(Duration::max());
++
++
++Time Clock::now()
++{
++ return now(__process__);
++}
++
++
++Time Clock::now(ProcessBase* process)
++{
++ synchronized (timeouts) {
++ if (Clock::paused()) {
++ if (process != NULL) {
++ if (clock::currents->count(process) != 0) {
++ return (*clock::currents)[process];
++ } else {
++ return (*clock::currents)[process] = clock::initial;
++ }
++ } else {
++ return clock::current;
++ }
++ }
++ }
++
++ // TODO(benh): Versus ev_now()?
++ double d = ev_time();
++ Try<Time> time = Time::create(d);
++
++ // TODO(xujyan): Move CHECK_SOME to libprocess and add CHECK_SOME
++ // here.
++ if (time.isError()) {
++ LOG(FATAL) << "Failed to create a Time from " << d << ": "
++ << time.error();
++ }
++ return time.get();
++}
++
++
++void Clock::pause()
++{
++ process::initialize(); // To make sure the libev watchers are ready.
++
++ synchronized (timeouts) {
++ if (!clock::paused) {
++ clock::initial = clock::current = now();
++ clock::paused = true;
++ VLOG(2) << "Clock paused at " << clock::initial;
++ }
++ }
++
++ // Note that after pausing the clock an existing libev timer might
++ // still fire (invoking handle_timeout), but since paused == true no
++ // "time" will actually have passed, so no timer will actually fire.
++}
++
++
++bool Clock::paused()
++{
++ return clock::paused;
++}
++
++
++void Clock::resume()
++{
++ process::initialize(); // To make sure the libev watchers are ready.
++
++ synchronized (timeouts) {
++ if (clock::paused) {
++ VLOG(2) << "Clock resumed at " << clock::current;
++ clock::paused = false;
++ clock::currents->clear();
++ update_timer = true;
++ ev_async_send(loop, &async_watcher);
++ }
++ }
++}
++
++
++void Clock::advance(const Duration& duration)
++{
++ synchronized (timeouts) {
++ if (clock::paused) {
++ clock::current += duration;
++ VLOG(2) << "Clock advanced (" << duration << ") to " << clock::current;
++ if (!update_timer) {
++ update_timer = true;
++ ev_async_send(loop, &async_watcher);
++ }
++ }
++ }
++}
++
++
++void Clock::advance(ProcessBase* process, const Duration& duration)
++{
++ synchronized (timeouts) {
++ if (clock::paused) {
++ Time current = now(process);
++ current += duration;
++ (*clock::currents)[process] = current;
++ VLOG(2) << "Clock of " << process->self() << " advanced (" << duration
++ << ") to " << current;
++ }
++ }
++}
++
++
++void Clock::update(const Time& time)
++{
++ synchronized (timeouts) {
++ if (clock::paused) {
++ if (clock::current < time) {
++ clock::current = Time(time);
++ VLOG(2) << "Clock updated to " << clock::current;
++ if (!update_timer) {
++ update_timer = true;
++ ev_async_send(loop, &async_watcher);
++ }
++ }
++ }
++ }
++}
++
++
++void Clock::update(ProcessBase* process, const Time& time)
++{
++ synchronized (timeouts) {
++ if (clock::paused) {
++ if (now(process) < time) {
++ VLOG(2) << "Clock of " << process->self() << " updated to " << time;
++ (*clock::currents)[process] = Time(time);
++ }
++ }
++ }
++}
++
++
++void Clock::order(ProcessBase* from, ProcessBase* to)
++{
++ update(to, now(from));
++}
++
++
++void Clock::settle()
++{
++ CHECK(clock::paused); // TODO(benh): Consider returning a bool instead.
++ process_manager->settle();
++}
++
++
++static Message* encode(const UPID& from,
++ const UPID& to,
++ const string& name,
++ const string& data = "")
++{
++ Message* message = new Message();
++ message->from = from;
++ message->to = to;
++ message->name = name;
++ message->body = data;
++ return message;
++}
++
++
++static void transport(Message* message, ProcessBase* sender = NULL)
++{
++ if (message->to.ip == __ip__ && message->to.port == __port__) {
++ // Local message.
++ process_manager->deliver(message->to, new MessageEvent(message), sender);
++ } else {
++ // Remote message.
++ socket_manager->send(message);
++ }
++}
++
++
++static bool libprocess(Request* request)
++{
++ return request->method == "POST" &&
++ request->headers.count("User-Agent") > 0 &&
++ request->headers["User-Agent"].find("libprocess/") == 0;
++}
++
++
++static Message* parse(Request* request)
++{
++ // TODO(benh): Do better error handling (to deal with a malformed
++ // libprocess message, malicious or otherwise).
++ const string& agent = request->headers["User-Agent"];
++ const string& identifier = "libprocess/";
++ size_t index = agent.find(identifier);
++ if (index != string::npos) {
++ // Okay, now determine 'from'.
++ const UPID from(agent.substr(index + identifier.size(), agent.size()));
++
++ // Now determine 'to'.
++ index = request->path.find('/', 1);
++ index = index != string::npos ? index - 1 : string::npos;
++ const UPID to(request->path.substr(1, index), __ip__, __port__);
++
++ // And now determine 'name'.
++ index = index != string::npos ? index + 2: request->path.size();
++ const string& name = request->path.substr(index);
++
++ VLOG(2) << "Parsed message name '" << name
++ << "' for " << to << " from " << from;
++
++ Message* message = new Message();
++ message->name = name;
++ message->from = from;
++ message->to = to;
++ message->body = request->body;
++
++ return message;
++ }
++
++ return NULL;
++}
++
++
++void handle_async(struct ev_loop* loop, ev_async* _, int revents)
++{
++ synchronized (watchers) {
++ // Start all the new I/O watchers.
++ while (!watchers->empty()) {
++ ev_io* watcher = watchers->front();
++ watchers->pop();
++ ev_io_start(loop, watcher);
++ }
++ }
++
++ synchronized (timeouts) {
++ if (update_timer) {
++ if (!timeouts->empty()) {
++ // Determine when the next timer should fire.
++ timeouts_watcher.repeat = (timeouts->begin()->first - Clock::now()).secs();
++
++ if (timeouts_watcher.repeat <= 0) {
++ // Feed the event now!
++ timeouts_watcher.repeat = 0;
++ ev_timer_again(loop, &timeouts_watcher);
++ ev_feed_event(loop, &timeouts_watcher, EV_TIMEOUT);
++ } else {
++ // Don't fire the timer if the clock is paused since we
++ // don't want time to advance (instead a call to
++ // clock::advance() will handle the timer).
++ if (Clock::paused() && timeouts_watcher.repeat > 0) {
++ timeouts_watcher.repeat = 0;
++ }
++
++ ev_timer_again(loop, &timeouts_watcher);
++ }
++ }
++
++ update_timer = false;
++ }
++ }
++}
++
++
++void handle_timeouts(struct ev_loop* loop, ev_timer* _, int revents)
++{
++ list<Timer> timedout;
++
++ synchronized (timeouts) {
++ Time now = Clock::now();
++
++ VLOG(3) << "Handling timeouts up to " << now;
++
++ foreachkey (const Time& timeout, *timeouts) {
++ if (timeout > now) {
++ break;
++ }
++
++ VLOG(3) << "Have timeout(s) at " << timeout;
++
++ // Record that we have pending timers to execute so the
++ // Clock::settle() operation can wait until we're done.
++ pending_timers = true;
++
++ foreach (const Timer& timer, (*timeouts)[timeout]) {
++ timedout.push_back(timer);
++ }
++ }
++
++ // Now erase the range of timeouts that timed out.
++ timeouts->erase(timeouts->begin(), timeouts->upper_bound(now));
++
++ // Okay, so the timeout for the next timer should not have fired.
++ CHECK(timeouts->empty() || (timeouts->begin()->first > now));
++
++ // Update the timer as necessary.
++ if (!timeouts->empty()) {
++ // Determine when the next timer should fire.
++ timeouts_watcher.repeat =
++ (timeouts->begin()->first - Clock::now()).secs();
++
++ if (timeouts_watcher.repeat <= 0) {
++ // Feed the event now!
++ timeouts_watcher.repeat = 0;
++ ev_timer_again(loop, &timeouts_watcher);
++ ev_feed_event(loop, &timeouts_watcher, EV_TIMEOUT);
++ } else {
++ // Don't fire the timer if the clock is paused since we don't
++ // want time to advance (instead a call to Clock::advance()
++ // will handle the timer).
++ if (Clock::paused() && timeouts_watcher.repeat > 0) {
++ timeouts_watcher.repeat = 0;
++ }
++
++ ev_timer_again(loop, &timeouts_watcher);
++ }
++ }
++
++ update_timer = false; // Since we might have a queued update_timer.
++ }
++
++ // Update current time of process (if it's present/valid). It might
++ // be necessary to actually add some more synchronization around
++ // this so that, for example, pausing and resuming the clock doesn't
++ // cause some processes to get thier current times updated and
++ // others not. Since ProcessManager::use acquires the 'processes'
++ // lock we had to move this out of the synchronized (timeouts) above
++ // since there was a deadlock with acquring 'processes' then
++ // 'timeouts' (reverse order) in ProcessManager::cleanup. Note that
++ // current time may be greater than the timeout if a local message
++ // was received (and happens-before kicks in).
++ if (Clock::paused()) {
++ foreach (const Timer& timer, timedout) {
++ if (ProcessReference process = process_manager->use(timer.creator())) {
++ Clock::update(process, timer.timeout().time());
++ }
++ }
++ }
++
++ // Invoke the timers that timed out (TODO(benh): Do this
++ // asynchronously so that we don't tie up the event thread!).
++ foreach (const Timer& timer, timedout) {
++ timer();
++ }
++
++ // Mark ourselves as done executing the timers since it's now safe
++ // for a call to Clock::settle() to check if there will be any
++ // future timeouts reached.
++ synchronized (timeouts) {
++ pending_timers = false;
++ }
++}
++
++
++void recv_data(struct ev_loop* loop, ev_io* watcher, int revents)
++{
++ DataDecoder* decoder = (DataDecoder*) watcher->data;
++
++ int s = watcher->fd;
++
++ while (true) {
++ const ssize_t size = 80 * 1024;
++ ssize_t length = 0;
++
++ char data[size];
++
++ length = recv(s, data, size, 0);
++
++ if (length < 0 && (errno == EINTR)) {
++ // Interrupted, try again now.
++ continue;
++ } else if (length < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
++ // Might block, try again later.
++ break;
++ } else if (length <= 0) {
++ // Socket error or closed.
++ if (length < 0) {
++ const char* error = strerror(errno);
++ VLOG(1) << "Socket error while receiving: " << error;
++ } else {
++ VLOG(1) << "Socket closed while receiving";
++ }
++ socket_manager->close(s);
++ delete decoder;
++ ev_io_stop(loop, watcher);
++ delete watcher;
++ break;
++ } else {
++ CHECK(length > 0);
++
++ // Decode as much of the data as possible into HTTP requests.
++ const deque<Request*>& requests = decoder->decode(data, length);
++
++ if (!requests.empty()) {
++ foreach (Request* request, requests) {
++ process_manager->handle(decoder->socket(), request);
++ }
++ } else if (requests.empty() && decoder->failed()) {
++ VLOG(1) << "Decoder error while receiving";
++ socket_manager->close(s);
++ delete decoder;
++ ev_io_stop(loop, watcher);
++ delete watcher;
++ break;
++ }
++ }
++ }
++}
++
++
++void send_data(struct ev_loop* loop, ev_io* watcher, int revents)
++{
++ DataEncoder* encoder = (DataEncoder*) watcher->data;
++
++ int s = watcher->fd;
++
++ while (true) {
++ const void* data;
++ size_t size;
++
++ data = encoder->next(&size);
++ CHECK(size > 0);
++
++ ssize_t length = send(s, data, size, MSG_NOSIGNAL);
++
++ if (length < 0 && (errno == EINTR)) {
++ // Interrupted, try again now.
++ encoder->backup(size);
++ continue;
++ } else if (length < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
++ // Might block, try again later.
++ encoder->backup(size);
++ break;
++ } else if (length <= 0) {
++ // Socket error or closed.
++ if (length < 0) {
++ const char* error = strerror(errno);
++ VLOG(1) << "Socket error while sending: " << error;
++ } else {
++ VLOG(1) << "Socket closed while sending";
++ }
++ socket_manager->close(s);
++ delete encoder;
++ ev_io_stop(loop, watcher);
++ delete watcher;
++ break;
++ } else {
++ CHECK(length > 0);
++
++ // Update the encoder with the amount sent.
++ encoder->backup(size - length);
++
++ // See if there is any more of the message to send.
++ if (encoder->remaining() == 0) {
++ delete encoder;
++
++ // Stop this watcher for now.
++ ev_io_stop(loop, watcher);
++
++ // Check for more stuff to send on socket.
++ Encoder* next = socket_manager->next(s);
++ if (next != NULL) {
++ watcher->data = next;
++ ev_io_init(watcher, next->sender(), s, EV_WRITE);
++ ev_io_start(loop, watcher);
++ } else {
++ // Nothing more to send right now, clean up.
++ delete watcher;
++ }
++ break;
++ }
++ }
++ }
++}
++
++
++void send_file(struct ev_loop* loop, ev_io* watcher, int revents)
++{
++ FileEncoder* encoder = (FileEncoder*) watcher->data;
++
++ int s = watcher->fd;
++
++ while (true) {
++ int fd;
++ off_t offset;
++ size_t size;
++
++ fd = encoder->next(&offset, &size);
++ CHECK(size > 0);
++
++ ssize_t length = os::sendfile(s, fd, offset, size);
++
++ if (length < 0 && (errno == EINTR)) {
++ // Interrupted, try again now.
++ encoder->backup(size);
++ continue;
++ } else if (length < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
++ // Might block, try again later.
++ encoder->backup(size);
++ break;
++ } else if (length <= 0) {
++ // Socket error or closed.
++ if (length < 0) {
++ const char* error = strerror(errno);
++ VLOG(1) << "Socket error while sending: " << error;
++ } else {
++ VLOG(1) << "Socket closed while sending";
++ }
++ socket_manager->close(s);
++ delete encoder;
++ ev_io_stop(loop, watcher);
++ delete watcher;
++ break;
++ } else {
++ CHECK(length > 0);
++
++ // Update the encoder with the amount sent.
++ encoder->backup(size - length);
++
++ // See if there is any more of the message to send.
++ if (encoder->remaining() == 0) {
++ delete encoder;
++
++ // Stop this watcher for now.
++ ev_io_stop(loop, watcher);
++
++ // Check for more stuff to send on socket.
++ Encoder* next = socket_manager->next(s);
++ if (next != NULL) {
++ watcher->data = next;
++ ev_io_init(watcher, next->sender(), s, EV_WRITE);
++ ev_io_start(loop, watcher);
++ } else {
++ // Nothing more to send right now, clean up.
++ delete watcher;
++ }
++ break;
++ }
++ }
++ }
++}
++
++
++void sending_connect(struct ev_loop* loop, ev_io* watcher, int revents)
++{
++ int s = watcher->fd;
++
++ // Now check that a successful connection was made.
++ int opt;
++ socklen_t optlen = sizeof(opt);
++
++ if (getsockopt(s, SOL_SOCKET, SO_ERROR, &opt, &optlen) < 0 || opt != 0) {
++ // Connect failure.
++ VLOG(1) << "Socket error while connecting";
++ socket_manager->close(s);
++ MessageEncoder* encoder = (MessageEncoder*) watcher->data;
++ delete encoder;
++ ev_io_stop(loop, watcher);
++ delete watcher;
++ } else {
++ // We're connected! Now let's do some sending.
++ ev_io_stop(loop, watcher);
++ ev_io_init(watcher, send_data, s, EV_WRITE);
++ ev_io_start(loop, watcher);
++ }
++}
++
++
++void receiving_connect(struct ev_loop* loop, ev_io* watcher, int revents)
++{
++ int s = watcher->fd;
++
++ // Now check that a successful connection was made.
++ int opt;
++ socklen_t optlen = sizeof(opt);
++
++ if (getsockopt(s, SOL_SOCKET, SO_ERROR, &opt, &optlen) < 0 || opt != 0) {
++ // Connect failure.
++ VLOG(1) << "Socket error while connecting";
++ socket_manager->close(s);
++ DataDecoder* decoder = (DataDecoder*) watcher->data;
++ delete decoder;
++ ev_io_stop(loop, watcher);
++ delete watcher;
++ } else {
++ // We're connected! Now let's do some receiving.
++ ev_io_stop(loop, watcher);
++ ev_io_init(watcher, recv_data, s, EV_READ);
++ ev_io_start(loop, watcher);
++ }
++}
++
++
++void accept(struct ev_loop* loop, ev_io* watcher, int revents)
++{
++ CHECK_EQ(__s__, watcher->fd);
++
++ sockaddr_in addr;
++ socklen_t addrlen = sizeof(addr);
++
++ int s = ::accept(__s__, (sockaddr*) &addr, &addrlen);
++
++ if (s < 0) {
++ return;
++ }
++
++ Try<Nothing> nonblock = os::nonblock(s);
++ if (nonblock.isError()) {
++ LOG_IF(INFO, VLOG_IS_ON(1)) << "Failed to accept, nonblock: "
++ << nonblock.error();
++ os::close(s);
++ return;
++ }
++
++ Try<Nothing> cloexec = os::cloexec(s);
++ if (cloexec.isError()) {
++ LOG_IF(INFO, VLOG_IS_ON(1)) << "Failed to accept, cloexec: "
++ << cloexec.error();
++ os::close(s);
++ return;
++ }
++
++ // Turn off Nagle (TCP_NODELAY) so pipelined requests don't wait.
++ int on = 1;
++ if (setsockopt(s, SOL_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) {
++ const char* error = strerror(errno);
++ VLOG(1) << "Failed to turn off the Nagle algorithm: " << error;
++ os::close(s);
++ } else {
++ // Inform the socket manager for proper bookkeeping.
++ const Socket& socket = socket_manager->accepted(s);
++
++ // Allocate and initialize the decoder and watcher.
++ DataDecoder* decoder = new DataDecoder(socket);
++
++ ev_io* watcher = new ev_io();
++ watcher->data = decoder;
++
++ ev_io_init(watcher, recv_data, s, EV_READ);
++ ev_io_start(loop, watcher);
++ }
++}
++
++
++void polled(struct ev_loop* loop, ev_io* watcher, int revents)
++{
++ Promise<short>* promise = (Promise<short>*) watcher->data;
++ promise->set(revents);
++ delete promise;
++
++ ev_io_stop(loop, watcher);
++ delete watcher;
++}
++
++
++void* serve(void* arg)
++{
++ ev_loop(((struct ev_loop*) arg), 0);
++
++ return NULL;
++}
++
++
++void* schedule(void* arg)
++{
++ do {
++ ProcessBase* process = process_manager->dequeue();
++ if (process == NULL) {
++ Gate::state_t old = gate->approach();
++ process = process_manager->dequeue();
++ if (process == NULL) {
++ gate->arrive(old); // Wait at gate if idle.
++ continue;
++ } else {
++ gate->leave();
++ }
++ }
++ process_manager->resume(process);
++ } while (true);
++}
++
++
++// We might find value in catching terminating signals at some point.
++// However, for now, adding signal handlers freely is not allowed
++// because they will clash with Java and Python virtual machines and
++// causes hard to debug crashes/segfaults.
++
++// void sigbad(int signal, struct sigcontext *ctx)
++// {
++// // Pass on the signal (so that a core file is produced).
++// struct sigaction sa;
++// sa.sa_handler = SIG_DFL;
++// sigemptyset(&sa.sa_mask);
++// sa.sa_flags = 0;
++// sigaction(signal, &sa, NULL);
++// raise(signal);
++// }
++
++
++void initialize(const string& delegate)
++{
++ // TODO(benh): Return an error if attempting to initialize again
++ // with a different delegate then originally specified.
++
++ // static pthread_once_t init = PTHREAD_ONCE_INIT;
++ // pthread_once(&init, ...);
++
++ static volatile bool initialized = false;
++ static volatile bool initializing = true;
++
++ // Try and do the initialization or wait for it to complete.
++ if (initialized && !initializing) {
++ return;
++ } else if (initialized && initializing) {
++ while (initializing);
++ return;
++ } else {
++ if (!__sync_bool_compare_and_swap(&initialized, false, true)) {
++ while (initializing);
++ return;
++ }
++ }
++
++// // Install signal handler.
++// struct sigaction sa;
++
++// sa.sa_handler = (void (*) (int)) sigbad;
++// sigemptyset (&sa.sa_mask);
++// sa.sa_flags = SA_RESTART;
++
++// sigaction (SIGTERM, &sa, NULL);
++// sigaction (SIGINT, &sa, NULL);
++// sigaction (SIGQUIT, &sa, NULL);
++// sigaction (SIGSEGV, &sa, NULL);
++// sigaction (SIGILL, &sa, NULL);
++// #ifdef SIGBUS
++// sigaction (SIGBUS, &sa, NULL);
++// #endif
++// #ifdef SIGSTKFLT
++// sigaction (SIGSTKFLT, &sa, NULL);
++// #endif
++// sigaction (SIGABRT, &sa, NULL);
++
++// sigaction (SIGFPE, &sa, NULL);
++
++#ifdef __sun__
++ /* Need to ignore this since we can't do MSG_NOSIGNAL on Solaris. */
++ signal(SIGPIPE, SIG_IGN);
++#endif // __sun__
++
++ // Create a new ProcessManager and SocketManager.
++ process_manager = new ProcessManager(delegate);
++ socket_manager = new SocketManager();
++
++ // Setup processing threads.
++ // We create no fewer than 8 threads because some tests require
++ // more worker threads than 'sysconf(_SC_NPROCESSORS_ONLN)' on
++ // computers with fewer cores.
++ // e.g. https://issues.apache.org/jira/browse/MESOS-818
++ //
++ // TODO(xujyan): Use a smarter algorithm to allocate threads.
++ // Allocating a static number of threads can cause starvation if
++ // there are more waiting Processes than the number of worker
++ // threads.
++ long cpus = std::max(8L, sysconf(_SC_NPROCESSORS_ONLN));
++
++ for (int i = 0; i < cpus; i++) {
++ pthread_t thread; // For now, not saving handles on our threads.
++ if (pthread_create(&thread, NULL, schedule, NULL) != 0) {
++ LOG(FATAL) << "Failed to initialize, pthread_create";
++ }
++ }
++
++ __ip__ = 0;
++ __port__ = 0;
++
++ char* value;
++
++ // Check environment for ip.
++ value = getenv("LIBPROCESS_IP");
++ if (value != NULL) {
++ int result = inet_pton(AF_INET, value, &__ip__);
++ if (result == 0) {
++ LOG(FATAL) << "LIBPROCESS_IP=" << value << " was unparseable";
++ } else if (result < 0) {
++ PLOG(FATAL) << "Failed to initialize, inet_pton";
++ }
++ }
++
++ // Check environment for port.
++ value = getenv("LIBPROCESS_PORT");
++ if (value != NULL) {
++ int result = atoi(value);
++ if (result < 0 || result > USHRT_MAX) {
++ LOG(FATAL) << "LIBPROCESS_PORT=" << value << " is not a valid port";
++ }
++ __port__ = result;
++ }
++
++ // Create a "server" socket for communicating with other nodes.
++ if ((__s__ = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
++ PLOG(FATAL) << "Failed to initialize, socket";
++ }
++
++ // Make socket non-blocking.
++ Try<Nothing> nonblock = os::nonblock(__s__);
++ if (nonblock.isError()) {
++ LOG(FATAL) << "Failed to initialize, nonblock: " << nonblock.error();
++ }
++
++ // Set FD_CLOEXEC flag.
++ Try<Nothing> cloexec = os::cloexec(__s__);
++ if (cloexec.isError()) {
++ LOG(FATAL) << "Failed to initialize, cloexec: " << cloexec.error();
++ }
++
++ // Allow address reuse.
++ int on = 1;
++ if (setsockopt(__s__, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
++ PLOG(FATAL) << "Failed to initialize, setsockopt(SO_REUSEADDR)";
++ }
++
++ // Set up socket.
++ sockaddr_in addr;
++ memset(&addr, 0, sizeof(addr));
++ addr.sin_family = PF_INET;
++ addr.sin_addr.s_addr = __ip__;
++ addr.sin_port = htons(__port__);
++
++ if (bind(__s__, (sockaddr*) &addr, sizeof(addr)) < 0) {
++ PLOG(FATAL) << "Failed to initialize, bind";
++ }
++
++ // Lookup and store assigned ip and assigned port.
++ socklen_t addrlen = sizeof(addr);
++ if (getsockname(__s__, (sockaddr*) &addr, &addrlen) < 0) {
++ PLOG(FATAL) << "Failed to initialize, getsockname";
++ }
++
++ __ip__ = addr.sin_addr.s_addr;
++ __port__ = ntohs(addr.sin_port);
++
++ // Lookup hostname if missing ip or if ip is 127.0.0.1 in case we
++ // actually have a valid external ip address. Note that we need only
++ // one ip address, so that other processes can send and receive and
++ // don't get confused as to whom they are sending to.
++ if (__ip__ == 0 || __ip__ == 2130706433) {
++ char hostname[512];
++
++ if (gethostname(hostname, sizeof(hostname)) < 0) {
++ LOG(FATAL) << "Failed to initialize, gethostname: "
++ << hstrerror(h_errno);
++ }
++
++ // Lookup IP address of local hostname.
++ hostent* he;
++
++ if ((he = gethostbyname2(hostname, AF_INET)) == NULL) {
++ LOG(FATAL) << "Failed to initialize, gethostbyname2: "
++ << hstrerror(h_errno);
++ }
++
++ __ip__ = *((uint32_t *) he->h_addr_list[0]);
++ }
++
++ if (listen(__s__, 500000) < 0) {
++ PLOG(FATAL) << "Failed to initialize, listen";
++ }
++
++ // Setup event loop.
++#ifdef __sun__
++ loop = ev_default_loop(EVBACKEND_POLL | EVBACKEND_SELECT);
++#else
++ loop = ev_default_loop(EVFLAG_AUTO);
++#endif // __sun__
++
++ ev_async_init(&async_watcher, handle_async);
++ ev_async_start(loop, &async_watcher);
++
++ ev_timer_init(&timeouts_watcher, handle_timeouts, 0., 2100000.0);
++ ev_timer_again(loop, &timeouts_watcher);
++
++ ev_io_init(&server_watcher, accept, __s__, EV_READ);
++ ev_io_start(loop, &server_watcher);
++
++// ev_child_init(&child_watcher, child_exited, pid, 0);
++// ev_child_start(loop, &cw);
++
++// /* Install signal handler. */
++// struct sigaction sa;
++
++// sa.sa_handler = ev_sighandler;
++// sigfillset (&sa.sa_mask);
++// sa.sa_flags = SA_RESTART; /* if restarting works we save one iteration */
++// sigaction (w->signum, &sa, 0);
++
++// sigemptyset (&sa.sa_mask);
++// sigaddset (&sa.sa_mask, w->signum);
++// sigprocmask (SIG_UNBLOCK, &sa.sa_mask, 0);
++
++ pthread_t thread; // For now, not saving handles on our threads.
++ if (pthread_create(&thread, NULL, serve, loop) != 0) {
++ LOG(FATAL) << "Failed to initialize, pthread_create";
++ }
++
++ // Need to set initialzing here so that we can actually invoke
++ // 'spawn' below for the garbage collector.
++ initializing = false;
++
++ // TODO(benh): Make sure creating the garbage collector, logging
++ // process, and profiler always succeeds and use supervisors to make
++ // sure that none terminate.
++
++ // Create global garbage collector process.
++ gc = spawn(new GarbageCollector());
++
++ // Create global help process.
++ help = spawn(new Help(), true);
++
++ // Create the global logging process.
++ spawn(new Logging(), true);
++
++ // Create the global profiler process.
++ spawn(new Profiler(), true);
++
++ // Create the global statistics.
++ value = getenv("LIBPROCESS_STATISTICS_WINDOW");
++ if (value != NULL) {
++ Try<Duration> window = Duration::parse(string(value));
++ if (window.isError()) {
++ LOG(FATAL) << "LIBPROCESS_STATISTICS_WINDOW=" << value
++ << " is not a valid duration: " << window.error();
++ }
++ statistics = new Statistics(window.get());
++ } else {
++ // TODO(bmahler): Investigate memory implications of this window
++ // size. We may also want to provide a maximum memory size rather than
++ // time window. Or, offload older data to disk, etc.
++ statistics = new Statistics(LIBPROCESS_STATISTICS_WINDOW);
++ }
++
++ // Initialize the mime types.
++ mime::initialize();
++
++ // Initialize the response statuses.
++ http::initialize();
++
++ // Add a route for getting process information.
++ lambda::function<Future<Response>(const Request&)> __processes__ =
++ lambda::bind(&ProcessManager::__processes__, process_manager, lambda::_1);
++
++ new Route("/__processes__", None(), __processes__);
++
++ char temp[INET_ADDRSTRLEN];
++ if (inet_ntop(AF_INET, (in_addr*) &__ip__, temp, INET_ADDRSTRLEN) == NULL) {
++ PLOG(FATAL) << "Failed to initialize, inet_ntop";
++ }
++
++ VLOG(1) << "libprocess is initialized on " << temp << ":" << __port__
++ << " for " << cpus << " cpus";
++}
++
++
++uint32_t ip()
++{
++ process::initialize();
++ return __ip__;
++}
++
++
++uint16_t port()
++{
++ process::initialize();
++ return __port__;
++}
++
++
++HttpProxy::HttpProxy(const Socket& _socket)
++ : ProcessBase(ID::generate("__http__")),
++ socket(_socket) {}
++
++
++HttpProxy::~HttpProxy()
++{
++ // Need to make sure response producers know not to continue to
++ // create a response (streaming or otherwise).
++ if (pipe.isSome()) {
++ os::close(pipe.get());
++ }
++ pipe = None();
++
++ while (!items.empty()) {
++ Item* item = items.front();
++
++ // Attempt to discard the future.
++ item->future->discard();
++
++ // But it might have already been ready ...
++ if (item->future->isReady()) {
++ const Response& response = item->future->get();
++ if (response.type == Response::PIPE) {
++ os::close(response.pipe);
++ }
++ }
++
++ items.pop();
++ delete item;
++ }
++}
++
++
++void HttpProxy::enqueue(const Response& response, const Request& request)
++{
++ handle(new Future<Response>(response), request);
++}
++
++
++void HttpProxy::handle(Future<Response>* future, const Request& request)
++{
++ items.push(new Item(request, future));
++
++ if (items.size() == 1) {
++ next();
++ }
++}
++
++
++void HttpProxy::next()
++{
++ if (items.size() > 0) {
++ // Wait for any transition of the future.
++ items.front()->future->onAny(
++ defer(self(), &HttpProxy::waited, lambda::_1));
++ }
++}
++
++
++void HttpProxy::waited(const Future<Response>& future)
++{
++ CHECK(items.size() > 0);
++ Item* item = items.front();
++
++ CHECK(future == *item->future);
++
++ // Process the item and determine if we're done or not (so we know
++ // whether to start waiting on the next responses).
++ bool processed = process(*item->future, item->request);
++
++ items.pop();
++ delete item;
++
++ if (processed) {
++ next();
++ }
++}
++
++
++bool HttpProxy::process(const Future<Response>& future, const Request& request)
++{
++ if (!future.isReady()) {
++ // TODO(benh): Consider handling other "states" of future
++ // (discarded, failed, etc) with different HTTP statuses.
++ socket_manager->send(ServiceUnavailable(), request, socket);
++ return true; // All done, can process next response.
++ }
++
++ Response response = future.get();
++
++ // If the response specifies a path, try and perform a sendfile.
++ if (response.type == Response::PATH) {
++ // Make sure no body is sent (this is really an error and
++ // should be reported and no response sent.
++ response.body.clear();
++
++ const string& path = response.path;
++ int fd = open(path.c_str(), O_RDONLY);
++ if (fd < 0) {
++ if (errno == ENOENT || errno == ENOTDIR) {
++ VLOG(1) << "Returning '404 Not Found' for path '" << path << "'";
++ socket_manager->send(NotFound(), request, socket);
++ } else {
++ const char* error = strerror(errno);
++ VLOG(1) << "Failed to send file at '" << path << "': " << error;
++ socket_manager->send(InternalServerError(), request, socket);
++ }
++ } else {
++ struct stat s; // Need 'struct' because of function named 'stat'.
++ if (fstat(fd, &s) != 0) {
++ const char* error = strerror(errno);
++ VLOG(1) << "Failed to send file at '" << path << "': " << error;
++ socket_manager->send(InternalServerError(), request, socket);
++ } else if (S_ISDIR(s.st_mode)) {
++ VLOG(1) << "Returning '404 Not Found' for directory '" << path << "'";
++ socket_manager->send(NotFound(), request, socket);
++ } else {
++ // While the user is expected to properly set a 'Content-Type'
++ // header, we fill in (or overwrite) 'Content-Length' header.
++ stringstream out;
++ out << s.st_size;
++ response.headers["Content-Length"] = out.str();
++
++ if (s.st_size == 0) {
++ socket_manager->send(response, request, socket);
++ return true; // All done, can process next request.
++ }
++
++ VLOG(1) << "Sending file at '" << path << "' with length " << s.st_size;
++
++ // TODO(benh): Consider a way to have the socket manager turn
++ // on TCP_CORK for both sends and then turn it off.
++ socket_manager->send(
++ new HttpResponseEncoder(socket, response, request),
++ true);
++
++ // Note the file descriptor gets closed by FileEncoder.
++ socket_manager->send(
++ new FileEncoder(socket, fd, s.st_size),
++ request.keepAlive);
++ }
++ }
++ } else if (response.type == Response::PIPE) {
++ // Make sure no body is sent (this is really an error and
++ // should be reported and no response sent.
++ response.body.clear();
++
++ // Make sure the pipe is nonblocking.
++ Try<Nothing> nonblock = os::nonblock(response.pipe);
++ if (nonblock.isError()) {
++ const char* error = strerror(errno);
++ VLOG(1) << "Failed make pipe nonblocking: " << error;
++ socket_manager->send(InternalServerError(), request, socket);
++ return true; // All done, can process next response.
++ }
++
++ // While the user is expected to properly set a 'Content-Type'
++ // header, we fill in (or overwrite) 'Transfer-Encoding' header.
++ response.headers["Transfer-Encoding"] = "chunked";
++
++ VLOG(1) << "Starting \"chunked\" streaming";
++
++ socket_manager->send(
++ new HttpResponseEncoder(socket, response, request),
++ true);
++
++ pipe = response.pipe;
++
++ io::poll(pipe.get(), io::READ).onAny(
++ defer(self(), &Self::stream, lambda::_1, request));
++
++ return false; // Streaming, don't process next response (yet)!
++ } else {
++ socket_manager->send(response, request, socket);
++ }
++
++ return true; // All done, can process next response.
++}
++
++
++void HttpProxy::stream(const Future<short>& poll, const Request& request)
++{
++ // TODO(benh): Use 'splice' on Linux.
++
++ CHECK(pipe.isSome());
++
++ bool finished = false; // Whether we're done streaming.
++
++ if (poll.isReady()) {
++ // Read and write.
++ CHECK(poll.get() == io::READ);
++ const size_t size = 4 * 1024; // 4K.
++ char data[size];
++ while (!finished) {
++ ssize_t length = ::read(pipe.get(), data, size);
++ if (length < 0 && (errno == EINTR)) {
++ // Interrupted, try again now.
++ continue;
++ } else if (length < 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
++ // Might block, try again later.
++ io::poll(pipe.get(), io::READ).onAny(
++ defer(self(), &Self::stream, lambda::_1, request));
++ break;
++ } else {
++ std::ostringstream out;
++ if (length <= 0) {
++ // Error or closed, treat both as closed.
++ if (length < 0) {
++ // Error.
++ const char* error = strerror(errno);
++ VLOG(1) << "Read error while streaming: " << error;
++ }
++ out << "0\r\n" << "\r\n";
++ finished = true;
++ } else {
++ // Data!
++ out << std::hex << length << "\r\n";
++ out.write(data, length);
++ out << "\r\n";
++ }
++
++ // We always persist the connection when we're not finished
++ // streaming.
++ socket_manager->send(
++ new DataEncoder(socket, out.str()),
++ finished ? request.keepAlive : true);
++ }
++ }
++ } else if (poll.isFailed()) {
++ VLOG(1) << "Failed to poll: " << poll.failure();
++ socket_manager->send(InternalServerError(), request, socket);
++ finished = true;
++ } else {
++ VLOG(1) << "Unexpected discarded future while polling";
++ socket_manager->send(InternalServerError(), request, socket);
++ finished = true;
++ }
++
++ if (finished) {
++ os::close(pipe.get());
++ pipe = None();
++ next();
++ }
++}
++
++
++SocketManager::SocketManager()
++{
++ synchronizer(this) = SYNCHRONIZED_INITIALIZER_RECURSIVE;
++}
++
++
++SocketManager::~SocketManager() {}
++
++
++Socket SocketManager::accepted(int s)
++{
++ synchronized (this) {
++ return sockets[s] = Socket(s);
++ }
++}
++
++
++void SocketManager::link(ProcessBase* process, const UPID& to)
++{
++ // TODO(benh): The semantics we want to support for link are such
++ // that if there is nobody to link to (local or remote) then an
++ // ExitedEvent gets generated. This functionality has only been
++ // implemented when the link is local, not remote. Of course, if
++ // there is nobody listening on the remote side, then this should
++ // work remotely ... but if there is someone listening remotely just
++ // not at that id, then it will silently continue executing.
++
++ CHECK(process != NULL);
++
++ Node node(to.ip, to.port);
++
++ synchronized (this) {
++ // Check if node is remote and there isn't a persistant link.
++ if ((node.ip != __ip__ || node.port != __port__)
++ && persists.count(node) == 0) {
++ // Okay, no link, lets create a socket.
++ int s;
++
++ if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
++ PLOG(FATAL) << "Failed to link, socket";
++ }
++
++ Try<Nothing> nonblock = os::nonblock(s);
++ if (nonblock.isError()) {
++ LOG(FATAL) << "Failed to link, nonblock: " << nonblock.error();
++ }
++
++ Try<Nothing> cloexec = os::cloexec(s);
++ if (cloexec.isError()) {
++ LOG(FATAL) << "Failed to link, cloexec: " << cloexec.error();
++ }
++
++ sockets[s] = Socket(s);
++ nodes[s] = node;
++
++ persists[node] = s;
++
++ // Allocate and initialize the decoder and watcher (we really
++ // only "receive" on this socket so that we can react when it
++ // gets closed and generate appropriate lost events).
++ DataDecoder* decoder = new DataDecoder(sockets[s]);
++
++ ev_io* watcher = new ev_io();
++ watcher->data = decoder;
++
++ // Try and connect to the node using this socket.
++ sockaddr_in addr;
++ memset(&addr, 0, sizeof(addr));
++ addr.sin_family = PF_INET;
++ addr.sin_port = htons(to.port);
++ addr.sin_addr.s_addr = to.ip;
++
++ if (connect(s, (sockaddr*) &addr, sizeof(addr)) < 0) {
++ if (errno != EINPROGRESS) {
++ PLOG(FATAL) << "Failed to link, connect";
++ }
++
++ // Wait for socket to be connected.
++ ev_io_init(watcher, receiving_connect, s, EV_WRITE);
++ } else {
++ ev_io_init(watcher, recv_data, s, EV_READ);
++ }
++
++ // Enqueue the watcher.
++ synchronized (watchers) {
++ watchers->push(watcher);
++ }
++
++ // Interrupt the loop.
++ ev_async_send(loop, &async_watcher);
++ }
++
++ links[to].insert(process);
++ }
++}
++
++
++PID<HttpProxy> SocketManager::proxy(const Socket& socket)
++{
++ HttpProxy* proxy = NULL;
++
++ synchronized (this) {
++ // This socket might have been asked to get closed (e.g., remote
++ // side hang up) while a process is attempting to handle an HTTP
++ // request. Thus, if there is no more socket, return an empty PID.
++ if (sockets.count(socket) > 0) {
++ if (proxies.count(socket) > 0) {
++ return proxies[socket]->self();
++ } else {
++ proxy = new HttpProxy(sockets[socket]);
++ proxies[socket] = proxy;
++ }
++ }
++ }
++
++ // Now check if we need to spawn a newly created proxy. Note that we
++ // need to do this outside of the synchronized block above to avoid
++ // a possible deadlock (because spawn eventually synchronizes on
++ // ProcessManager and ProcessManager::cleanup synchronizes on
++ // ProcessManager and then SocketManager, so a deadlock results if
++ // we do spawn within the synchronized block above).
++ if (proxy != NULL) {
++ return spawn(proxy, true);
++ }
++
++ return PID<HttpProxy>();
++}
++
++
++void SocketManager::send(Encoder* encoder, bool persist)
++{
++ CHECK(encoder != NULL);
++
++ synchronized (this) {
++ if (sockets.count(encoder->socket()) > 0) {
++ // Update whether or not this socket should get disposed after
++ // there is no more data to send.
++ if (!persist) {
++ dispose.insert(encoder->socket());
++ }
++
++ if (outgoing.count(encoder->socket()) > 0) {
++ outgoing[encoder->socket()].push(encoder);
++ } else {
++ // Initialize the outgoing queue.
++ outgoing[encoder->socket()];
++
++ // Allocate and initialize the watcher.
++ ev_io* watcher = new ev_io();
++ watcher->data = encoder;
++
++ ev_io_init(watcher, encoder->sender(), encoder->socket(), EV_WRITE);
++
++ synchronized (watchers) {
++ watchers->push(watcher);
++ }
++
++ ev_async_send(loop, &async_watcher);
++ }
++ } else {
++ VLOG(1) << "Attempting to send on a no longer valid socket!";
++ delete encoder;
++ }
++ }
++}
++
++
++void SocketManager::send(
++ const Response& response,
++ const Request& request,
++ const Socket& socket)
++{
++ bool persist = request.keepAlive;
++
++ // Don't persist the connection if the headers include
++ // 'Connection: close'.
++ if (response.headers.contains("Connection")) {
++ if (response.headers.get("Connection").get() == "close") {
++ persist = false;
++ }
++ }
++
++ send(new HttpResponseEncoder(socket, response, request), persist);
++}
++
++
++void SocketManager::send(Message* message)
++{
++ CHECK(message != NULL);
++
++ Node node(message->to.ip, message->to.port);
++
++ synchronized (this) {
++ // Check if there is already a socket.
++ bool persist = persists.count(node) > 0;
++ bool temp = temps.count(node) > 0;
++ if (persist || temp) {
++ int s = persist ? persists[node] : temps[node];
++ CHECK(sockets.count(s) > 0);
++ send(new MessageEncoder(sockets[s], message), persist);
++ } else {
++ // No peristant or temporary socket to the node currently
++ // exists, so we create a temporary one.
++ int s;
++
++ if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
++ PLOG(FATAL) << "Failed to send, socket";
++ }
++
++ Try<Nothing> nonblock = os::nonblock(s);
++ if (nonblock.isError()) {
++ LOG(FATAL) << "Failed to send, nonblock: " << nonblock.error();
++ }
++
++ Try<Nothing> cloexec = os::cloexec(s);
++ if (cloexec.isError()) {
++ LOG(FATAL) << "Failed to send, cloexec: " << cloexec.error();
++ }
++
++ sockets[s] = Socket(s);
++ nodes[s] = node;
++ temps[node] = s;
++
++ dispose.insert(s);
++
++ // Initialize the outgoing queue.
++ outgoing[s];
++
++ // Allocate and initialize the watcher.
++ ev_io* watcher = new ev_io();
++ watcher->data = new MessageEncoder(sockets[s], message);
++
++ // Try and connect to the node using this socket.
++ sockaddr_in addr;
++ memset(&addr, 0, sizeof(addr));
++ addr.sin_family = PF_INET;
++ addr.sin_port = htons(message->to.port);
++ addr.sin_addr.s_addr = message->to.ip;
++
++ if (connect(s, (sockaddr*) &addr, sizeof(addr)) < 0) {
++ if (errno != EINPROGRESS) {
++ PLOG(FATAL) << "Failed to send, connect";
++ }
++
++ // Initialize watcher for connecting.
++ ev_io_init(watcher, sending_connect, s, EV_WRITE);
++ } else {
++ // Initialize watcher for sending.
++ ev_io_init(watcher, send_data, s, EV_WRITE);
++ }
++
++ // Enqueue the watcher.
++ synchronized (watchers) {
++ watchers->push(watcher);
++ }
++
++ ev_async_send(loop, &async_watcher);
++ }
++ }
++}
++
++
++Encoder* SocketManager::next(int s)
++{
++ HttpProxy* proxy = NULL; // Non-null if needs to be terminated.
++
++ synchronized (this) {
++ // We cannot assume 'sockets.count(s) > 0' here because it's
++ // possible that 's' has been removed with a a call to
++ // SocketManager::close. For example, it could be the case that a
++ // socket has gone to CLOSE_WAIT and the call to 'recv' in
++ // recv_data returned 0 causing SocketManager::close to get
++ // invoked. Later a call to 'send' or 'sendfile' (e.g., in
++ // send_data or send_file) can "succeed" (because the socket is
++ // not "closed" yet because there are still some Socket
++ // references, namely the reference being used in send_data or
++ // send_file!). However, when SocketManger::next is actually
++ // invoked we find out there there is no more data and thus stop
++ // sending.
++ // TODO(benh): Should we actually finish sending the data!?
++ if (sockets.count(s) > 0) {
++ CHECK(outgoing.count(s) > 0);
++
++ if (!outgoing[s].empty()) {
++ // More messages!
++ Encoder* encoder = outgoing[s].front();
++ outgoing[s].pop();
++ return encoder;
++ } else {
++ // No more messages ... erase the outgoing queue.
++ outgoing.erase(s);
++
++ if (dispose.count(s) > 0) {
++ // This is either a temporary socket we created or it's a
++ // socket that we were receiving data from and possibly
++ // sending HTTP responses back on. Clean up either way.
++ if (nodes.count(s) > 0) {
++ const Node& node = nodes[s];
++ CHECK(temps.count(node) > 0 && temps[node] == s);
++ temps.erase(node);
++ nodes.erase(s);
++ }
++
++ if (proxies.count(s) > 0) {
++ proxy = proxies[s];
++ proxies.erase(s);
++ }
++
++ dispose.erase(s);
++ sockets.erase(s);
++
++ // We don't actually close the socket (we wait for the Socket
++ // abstraction to close it once there are no more references),
++ // but we do shutdown the receiving end so any DataDecoder
++ // will get cleaned up (which might have the last reference).
++ shutdown(s, SHUT_RD);
++ }
++ }
++ }
++ }
++
++ // We terminate the proxy outside the synchronized block to avoid
++ // possible deadlock between the ProcessManager and SocketManager
++ // (see comment in SocketManager::proxy for more information).
++ if (proxy != NULL) {
++ terminate(proxy);
++ }
++
++ return NULL;
++}
++
++
++void SocketManager::close(int s)
++{
++ HttpProxy* proxy = NULL; // Non-null if needs to be terminated.
++
++ synchronized (this) {
++ // This socket might not be active if it was already asked to get
++ // closed (e.g., a write on the socket failed so we try and close
++ // it and then later the read side of the socket gets closed so we
++ // try and close it again). Thus, ignore the request if we don't
++ // know about the socket.
++ if (sockets.count(s) > 0) {
++ // Clean up any remaining encoders for this socket.
++ if (outgoing.count(s) > 0) {
++ while (!outgoing[s].empty()) {
++ Encoder* encoder = outgoing[s].front();
++ delete encoder;
++ outgoing[s].pop();
++ }
++
++ outgoing.erase(s);
++ }
++
++ // Clean up after sockets used for node communication.
++ if (nodes.count(s) > 0) {
++ const Node& node = nodes[s];
++
++ // Don't bother invoking exited unless socket was persistant.
++ if (persists.count(node) > 0 && persists[node] == s) {
++ persists.erase(node);
++ exited(node); // Generate ExitedEvent(s)!
++ } else if (temps.count(node) > 0 && temps[node] == s) {
++ temps.erase(node);
++ }
++
++ nodes.erase(s);
++ }
++
++ // Clean up any proxy associated with this socket.
++ if (proxies.count(s) > 0) {
++ proxy = proxies[s];
++ proxies.erase(s);
++ }
++
++ dispose.erase(s);
++ sockets.erase(s);
++ }
++ }
++
++ // We terminate the proxy outside the synchronized block to avoid
++ // possible deadlock between the ProcessManager and SocketManager.
++ if (proxy != NULL) {
++ terminate(proxy);
++ }
++
++ // Note that we don't actually:
++ //
++ // close(s);
++ //
++ // Because, for example, there could be a race between an HttpProxy
++ // trying to do send a response with SocketManager::send() or a
++ // process might be responding to another Request (e.g., trying
++ // to do a sendfile) since these things may be happening
++ // asynchronously we can't close the socket yet, because it might
++ // get reused before any of the above things have finished, and then
++ // we'll end up sending data on the wrong socket! Instead, we rely
++ // on the last reference of our Socket object to close the
++ // socket. Note, however, that since socket is no longer in
++ // 'sockets' any attempt to send with it will just get ignored.
++}
++
++
++void SocketManager::exited(const Node& node)
++{
++ // TODO(benh): It would be cleaner if this routine could call back
++ // into ProcessManager ... then we wouldn't have to convince
++ // ourselves that the accesses to each Process object will always be
++ // valid.
++ synchronized (this) {
++ list<UPID> removed;
++ // Look up all linked processes.
++ foreachpair (const UPID& linkee, set<ProcessBase*>& processes, links) {
++ if (linkee.ip == node.ip && linkee.port == node.port) {
++ foreach (ProcessBase* linker, processes) {
++ linker->enqueue(new ExitedEvent(linkee));
++ }
++ removed.push_back(linkee);
++ }
++ }
++
++ foreach (const UPID& pid, removed) {
++ links.erase(pid);
++ }
++ }
++}
++
++
++void SocketManager::exited(ProcessBase* process)
++{
++ // An exited event is enough to cause the process to get deleted
++ // (e.g., by the garbage collector), which means we can't
++ // dereference process (or even use the address) after we enqueue at
++ // least one exited event. Thus, we save the process pid.
++ const UPID pid = process->pid;
++
++ // Likewise, we need to save the current time of the process so we
++ // can update the clocks of linked processes as appropriate.
++ const Time time = Clock::now(process);
++
++ synchronized (this) {
++ // Iterate through the links, removing any links the process might
++ // have had and creating exited events for any linked processes.
++ foreachpair (const UPID& linkee, set<ProcessBase*>& processes, links) {
++ processes.erase(process);
++
++ if (linkee == pid) {
++ foreach (ProcessBase* linker, processes) {
++ CHECK(linker != process) << "Process linked with itself";
++ synchronized (timeouts) {
++ if (Clock::paused()) {
++ Clock::update(linker, time);
++ }
++ }
++ linker->enqueue(new ExitedEvent(linkee));
++ }
++ }
++ }
++
++ links.erase(pid);
++ }
++}
++
++
++ProcessManager::ProcessManager(const string& _delegate)
++ : delegate(_delegate)
++{
++ synchronizer(processes) = SYNCHRONIZED_INITIALIZER_RECURSIVE;
++ synchronizer(runq) = SYNCHRONIZED_INITIALIZER_RECURSIVE;
++ running = 0;
++ __sync_synchronize(); // Ensure write to 'running' visible in other threads.
++}
++
++
++ProcessManager::~ProcessManager() {}
++
++
++ProcessReference ProcessManager::use(const UPID& pid)
++{
++ if (pid.ip == __ip__ && pid.port == __port__) {
++ synchronized (processes) {
++ if (processes.count(pid.id) > 0) {
++ // Note that the ProcessReference constructor _must_ get
++ // called while holding the lock on processes so that waiting
++ // for references is atomic (i.e., race free).
++ return ProcessReference(processes[pid.id]);
++ }
++ }
++ }
++
++ return ProcessReference(NULL);
++}
++
++
++bool ProcessManager::handle(
++ const Socket& socket,
++ Request* request)
++{
++ CHECK(request != NULL);
++
++ // Check if this is a libprocess request (i.e., 'User-Agent:
++ // libprocess/id at ip:port') and if so, parse as a message.
++ if (libprocess(request)) {
++ Message* message = parse(request);
++ if (message != NULL) {
++ delete request;
++ // TODO(benh): Use the sender PID in order to capture
++ // happens-before timing relationships for testing.
++ return deliver(message->to, new MessageEvent(message));
++ }
++
++ VLOG(1) << "Failed to handle libprocess request: "
++ << request->method << " " << request->path
++ << " (User-Agent: " << request->headers["User-Agent"] << ")";
++
++ delete request;
++ return false;
++ }
++
++ // Treat this as an HTTP request. Start by checking that the path
++ // starts with a '/' (since the code below assumes as much).
++ if (request->path.find('/') != 0) {
++ VLOG(1) << "Returning '400 Bad Request' for '" << request->path << "'";
++
++ // Get the HttpProxy pid for this socket.
++ PID<HttpProxy> proxy = socket_manager->proxy(socket);
++
++ // Enqueue the response with the HttpProxy so that it respects the
++ // order of requests to account for HTTP/1.1 pipelining.
++ dispatch(proxy, &HttpProxy::enqueue, BadRequest(), *request);
++
++ // Cleanup request.
++ delete request;
++ return false;
++ }
++
++ // Ignore requests with relative paths (i.e., contain "/..").
++ if (request->path.find("/..") != string::npos) {
++ VLOG(1) << "Returning '404 Not Found' for '" << request->path
++ << "' (ignoring requests with relative paths)";
++
++ // Get the HttpProxy pid for this socket.
++ PID<HttpProxy> proxy = socket_manager->proxy(socket);
++
++ // Enqueue the response with the HttpProxy so that it respects the
++ // order of requests to account for HTTP/1.1 pipelining.
++ dispatch(proxy, &HttpProxy::enqueue, NotFound(), *request);
++
++ // Cleanup request.
++ delete request;
++ return false;
++ }
++
++ // Split the path by '/'.
++ vector<string> tokens = strings::tokenize(request->path, "/");
++
++ // Try and determine a receiver, otherwise try and delegate.
++ ProcessReference receiver;
++
++ if (tokens.size() == 0 && delegate != "") {
++ request->path = "/" + delegate;
++ receiver = use(UPID(delegate, __ip__, __port__));
++ } else if (tokens.size() > 0) {
++ receiver = use(UPID(tokens[0], __ip__, __port__));
++ }
++
++ if (!receiver && delegate != "") {
++ // Try and delegate the request.
++ request->path = "/" + delegate + request->path;
++ receiver = use(UPID(delegate, __ip__, __port__));
++ }
++
++ if (receiver) {
++ // TODO(benh): Use the sender PID in order to capture
++ // happens-before timing relationships for testing.
++ return deliver(receiver, new HttpEvent(socket, request));
++ }
++
++ // This has no receiver, send error response.
++ VLOG(1) << "Returning '404 Not Found' for '" << request->path << "'";
++
++ // Get the HttpProxy pid for this socket.
++ PID<HttpProxy> proxy = socket_manager->proxy(socket);
++
++ // Enqueue the response with the HttpProxy so that it respects the
++ // order of requests to account for HTTP/1.1 pipelining.
++ dispatch(proxy, &HttpProxy::enqueue, NotFound(), *request);
++
++ // Cleanup request.
++ delete request;
++ return false;
++}
++
++
++bool ProcessManager::deliver(
++ ProcessBase* receiver,
++ Event* event,
++ ProcessBase* sender)
++{
++ CHECK(event != NULL);
++
++ // If we are using a manual clock then update the current time of
++ // the receiver using the sender if necessary to preserve the
++ // happens-before relationship between the sender and receiver. Note
++ // that the assumption is that the sender remains valid for at least
++ // the duration of this routine (so that we can look up it's current
++ // time).
++ if (Clock::paused()) {
++ synchronized (timeouts) {
++ if (Clock::paused()) {
++ if (sender != NULL) {
++ Clock::order(sender, receiver);
++ } else {
++ Clock::update(receiver, Clock::now());
++ }
++ }
++ }
++ }
++
++ receiver->enqueue(event);
++
++ return true;
++}
++
++
++bool ProcessManager::deliver(
++ const UPID& to,
++ Event* event,
++ ProcessBase* sender)
++{
++ CHECK(event != NULL);
++
++ if (ProcessReference receiver = use(to)) {
++ return deliver(receiver, event, sender);
++ }
++
++ delete event;
++ return false;
++}
++
++
++UPID ProcessManager::spawn(ProcessBase* process, bool manage)
++{
++ CHECK(process != NULL);
++
++ synchronized (processes) {
++ if (processes.count(process->pid.id) > 0) {
++ return UPID();
++ } else {
++ processes[process->pid.id] = process;
++ }
++ }
++
++ // Use the garbage collector if requested.
++ if (manage) {
++ dispatch(gc, &GarbageCollector::manage<ProcessBase>, process);
++ }
++
++ // We save the PID before enqueueing the process to avoid the race
++ // condition that occurs when a user has a very short process and
++ // the process gets run and cleaned up before we return from enqueue
++ // (e.g., when 'manage' is set to true).
++ UPID pid = process->self();
++
++ // Add process to the run queue (so 'initialize' will get invoked).
++ enqueue(process);
++
++ VLOG(2) << "Spawned process " << pid;
++
++ return pid;
++}
++
++
++void ProcessManager::resume(ProcessBase* process)
++{
++ __process__ = process;
++
++ VLOG(2) << "Resuming " << process->pid << " at " << Clock::now();
++
++ bool terminate = false;
++ bool blocked = false;
++
++ CHECK(process->state == ProcessBase::BOTTOM ||
++ process->state == ProcessBase::READY);
++
++ if (process->state == ProcessBase::BOTTOM) {
++ process->state = ProcessBase::RUNNING;
++ try { process->initialize(); }
++ catch (...) { terminate = true; }
++ }
++
++ while (!terminate && !blocked) {
++ Event* event = NULL;
++
++ process->lock();
++ {
++ if (process->events.size() > 0) {
++ event = process->events.front();
++ process->events.pop_front();
++ process->state = ProcessBase::RUNNING;
++ } else {
++ process->state = ProcessBase::BLOCKED;
++ blocked = true;
++ }
++ }
++ process->unlock();
++
++ if (!blocked) {
++ CHECK(event != NULL);
++
++ // Determine if we should filter this event.
++ synchronized (filterer) {
++ if (filterer != NULL) {
++ bool filter = false;
++ struct FilterVisitor : EventVisitor
++ {
++ FilterVisitor(bool* _filter) : filter(_filter) {}
++
++ virtual void visit(const MessageEvent& event)
++ {
++ *filter = filterer->filter(event);
++ }
++
++ virtual void visit(const DispatchEvent& event)
++ {
++ *filter = filterer->filter(event);
++ }
++
++ virtual void visit(const HttpEvent& event)
++ {
++ *filter = filterer->filter(event);
++ }
++
++ virtual void visit(const ExitedEvent& event)
++ {
++ *filter = filterer->filter(event);
++ }
++
++ bool* filter;
++ } visitor(&filter);
++
++ event->visit(&visitor);
++
++ if (filter) {
++ delete event;
++ continue; // Try and execute the next event.
++ }
++ }
++ }
++
++ // Determine if we should terminate.
++ terminate = event->is<TerminateEvent>();
++
++ // Now service the event.
++ try {
++ process->serve(*event);
++ } catch (const std::exception& e) {
++ std::cerr << "libprocess: " << process->pid
++ << " terminating due to "
++ << e.what() << std::endl;
++ terminate = true;
++ } catch (...) {
++ std::cerr << "libprocess: " << process->pid
++ << " terminating due to unknown exception" << std::endl;
++ terminate = true;
++ }
++
++ delete event;
++
++ if (terminate) {
++ cleanup(process);
++ }
++ }
++ }
++
++ __process__ = NULL;
++
++ CHECK_GE(running, 1);
++ __sync_fetch_and_sub(&running, 1);
++}
++
++
++void ProcessManager::cleanup(ProcessBase* process)
++{
++ VLOG(2) << "Cleaning up " << process->pid;
++
++ // First, set the terminating state so no more events will get
++ // enqueued and delete al the pending events. We want to delete the
++ // events before we hold the processes lock because deleting an
++ // event could cause code outside libprocess to get executed which
++ // might cause a deadlock with the processes lock. Likewise,
++ // deleting the events now rather than later has the nice property
++ // of making sure that any events that might have gotten enqueued on
++ // the process we are cleaning up will get dropped (since it's
++ // terminating) and eliminates the potential of enqueueing them on
++ // another process that gets spawned with the same PID.
++ deque<Event*> events;
++
++ process->lock();
++ {
++ process->state = ProcessBase::TERMINATING;
++ events = process->events;
++ process->events.clear();
++ }
++ process->unlock();
++
++ // Delete pending events.
++ while (!events.empty()) {
++ Event* event = events.front();
++ events.pop_front();
++ delete event;
++ }
++
++ // Possible gate non-libprocess threads are waiting at.
++ Gate* gate = NULL;
++
++ // Remove process.
++ synchronized (processes) {
++ // Wait for all process references to get cleaned up.
++ while (process->refs > 0) {
++ asm ("pause");
++ __sync_synchronize();
++ }
++
++ process->lock();
++ {
++ CHECK(process->events.empty());
++
++ processes.erase(process->pid.id);
++
++ // Lookup gate to wake up waiting threads.
++ map<ProcessBase*, Gate*>::iterator it = gates.find(process);
++ if (it != gates.end()) {
++ gate = it->second;
++ // N.B. The last thread that leaves the gate also free's it.
++ gates.erase(it);
++ }
++
++ CHECK(process->refs == 0);
++ process->state = ProcessBase::TERMINATED;
++ }
++ process->unlock();
++
++ // Note that we don't remove the process from the clock during
++ // cleanup, but rather the clock is reset for a process when it is
++ // created (see ProcessBase::ProcessBase). We do this so that
++ // SocketManager::exited can access the current time of the
++ // process to "order" exited events. TODO(benh): It might make
++ // sense to consider storing the time of the process as a field of
++ // the class instead.
++
++ // Now we tell the socket manager about this process exiting so
++ // that it can create exited events for linked processes. We
++ // _must_ do this while synchronized on processes because
++ // otherwise another process could attempt to link this process
++ // and SocketManger::link would see that the processes doesn't
++ // exist when it attempts to get a ProcessReference (since we
++ // removed the process above) thus causing an exited event, which
++ // could cause the process to get deleted (e.g., the garbage
++ // collector might link _after_ the process has already been
++ // removed from processes thus getting an exited event but we
++ // don't want that exited event to fire and actually delete the
++ // process until after we have used the process in
++ // SocketManager::exited).
++ socket_manager->exited(process);
++
++ // ***************************************************************
++ // At this point we can no longer dereference the process since it
++ // might already be deallocated (e.g., by the garbage collector).
++ // ***************************************************************
++
++ // Note that we need to open the gate while synchronized on
++ // processes because otherwise we might _open_ the gate before
++ // another thread _approaches_ the gate causing that thread to
++ // wait on _arrival_ to the gate forever (see
++ // ProcessManager::wait).
++ if (gate != NULL) {
++ gate->open();
++ }
++ }
++}
++
++
++void ProcessManager::link(ProcessBase* process, const UPID& to)
++{
++ // Check if the pid is local.
++ if (!(to.ip == __ip__ && to.port == __port__)) {
++ socket_manager->link(process, to);
++ } else {
++ // Since the pid is local we want to get a reference to it's
++ // underlying process so that while we are invoking the link
++ // manager we don't miss sending a possible ExitedEvent.
++ if (ProcessReference _ = use(to)) {
++ socket_manager->link(process, to);
++ } else {
++ // Since the pid isn't valid it's process must have already died
++ // (or hasn't been spawned yet) so send a process exit message.
++ process->enqueue(new ExitedEvent(to));
++ }
++ }
++}
++
++
++void ProcessManager::terminate(
++ const UPID& pid,
++ bool inject,
++ ProcessBase* sender)
++{
++ if (ProcessReference process = use(pid)) {
++ if (Clock::paused()) {
++ synchronized (timeouts) {
++ if (Clock::paused()) {
++ if (sender != NULL) {
++ Clock::order(sender, process);
++ } else {
++ Clock::update(process, Clock::now());
++ }
++ }
++ }
++ }
++
++ if (sender != NULL) {
++ process->enqueue(new TerminateEvent(sender->self()), inject);
++ } else {
++ process->enqueue(new TerminateEvent(UPID()), inject);
++ }
++ }
++}
++
++
++bool ProcessManager::wait(const UPID& pid)
++{
++ // We use a gate for waiters. A gate is single use. That is, a new
++ // gate is created when the first thread shows up and wants to wait
++ // for a process that currently has no gate. Once that process
++ // exits, the last thread to leave the gate will also clean it
++ // up. Note that a gate will never get more threads waiting on it
++ // after it has been opened, since the process should no longer be
++ // valid and therefore will not have an entry in 'processes'.
++
++ Gate* gate = NULL;
++ Gate::state_t old;
++
++ ProcessBase* process = NULL; // Set to non-null if we donate thread.
++
++ // Try and approach the gate if necessary.
++ synchronized (processes) {
++ if (processes.count(pid.id) > 0) {
++ process = processes[pid.id];
++ CHECK(process->state != ProcessBase::TERMINATED);
++
++ // Check and see if a gate already exists.
++ if (gates.find(process) == gates.end()) {
++ gates[process] = new Gate();
++ }
++
++ gate = gates[process];
++ old = gate->approach();
++
++ // Check if it is runnable in order to donate this thread.
++ if (process->state == ProcessBase::BOTTOM ||
++ process->state == ProcessBase::READY) {
++ synchronized (runq) {
++ list<ProcessBase*>::iterator it =
++ find(runq.begin(), runq.end(), process);
++ if (it != runq.end()) {
++ runq.erase(it);
++ } else {
++ // Another thread has resumed the process ...
++ process = NULL;
++ }
++ }
++ } else {
++ // Process is not runnable, so no need to donate ...
++ process = NULL;
++ }
++ }
++ }
++
++ if (process != NULL) {
++ VLOG(2) << "Donating thread to " << process->pid << " while waiting";
++ ProcessBase* donator = __process__;
++ __sync_fetch_and_add(&running, 1);
++ process_manager->resume(process);
++ __process__ = donator;
++ }
++
++ // TODO(benh): Donating only once may not be sufficient, so we might
++ // still deadlock here ... perhaps warn if that's the case?
++
++ // Now arrive at the gate and wait until it opens.
++ if (gate != NULL) {
++ gate->arrive(old);
++
++ if (gate->empty()) {
++ delete gate;
++ }
++
++ return true;
++ }
++
++ return false;
++}
++
++
++void ProcessManager::enqueue(ProcessBase* process)
++{
++ CHECK(process != NULL);
++
++ // TODO(benh): Check and see if this process has it's own thread. If
++ // it does, push it on that threads runq, and wake up that thread if
++ // it's not running. Otherwise, check and see which thread this
++ // process was last running on, and put it on that threads runq.
++
++ synchronized (runq) {
++ CHECK(find(runq.begin(), runq.end(), process) == runq.end());
++ runq.push_back(process);
++ }
++
++ // Wake up the processing thread if necessary.
++ gate->open();
++}
++
++
++ProcessBase* ProcessManager::dequeue()
++{
++ // TODO(benh): Remove a process from this thread's runq. If there
++ // are no processes to run, and this is not a dedicated thread, then
++ // steal one from another threads runq.
++
++ ProcessBase* process = NULL;
++
++ synchronized (runq) {
++ if (!runq.empty()) {
++ process = runq.front();
++ runq.pop_front();
++ // Increment the running count of processes in order to support
++ // the Clock::settle() operation (this must be done atomically
++ // with removing the process from the runq).
++ __sync_fetch_and_add(&running, 1);
++ }
++ }
++
++ return process;
++}
++
++
++void ProcessManager::settle()
++{
++ bool done = true;
++ do {
++ os::sleep(Milliseconds(10));
++ done = true;
++ // Hopefully this is the only place we acquire both these locks.
++ synchronized (runq) {
++ synchronized (timeouts) {
++ CHECK(Clock::paused()); // Since another thread could resume the clock!
++
++ if (!runq.empty()) {
++ done = false;
++ }
++
++ __sync_synchronize(); // Read barrier for 'running'.
++ if (running > 0) {
++ done = false;
++ }
++
++ if (timeouts->size() > 0 &&
++ timeouts->begin()->first <= clock::current) {
++ done = false;
++ }
++
++ if (pending_timers) {
++ done = false;
++ }
++ }
++ }
++ } while (!done);
++}
++
++
++Future<Response> ProcessManager::__processes__(const Request&)
++{
++ JSON::Array array;
++
++ synchronized (processes) {
++ foreachvalue (const ProcessBase* process, process_manager->processes) {
++ JSON::Object object;
++ object.values["id"] = process->pid.id;
++
++ JSON::Array events;
++
++ struct JSONVisitor : EventVisitor
++ {
++ JSONVisitor(JSON::Array* _events) : events(_events) {}
++
++ virtual void visit(const MessageEvent& event)
++ {
++ JSON::Object object;
++ object.values["type"] = "MESSAGE";
++
++ const Message& message = *event.message;
++
++ object.values["name"] = message.name;
++ object.values["from"] = string(message.from);
++ object.values["to"] = string(message.to);
++ object.values["body"] = message.body;
++
++ events->values.push_back(object);
++ }
++
++ virtual void visit(const HttpEvent& event)
++ {
++ JSON::Object object;
++ object.values["type"] = "HTTP";
++
++ const Request& request = *event.request;
++
++ object.values["method"] = request.method;
++ object.values["url"] = request.url;
++
++ events->values.push_back(object);
++ }
++
++ virtual void visit(const DispatchEvent& event)
++ {
++ JSON::Object object;
++ object.values["type"] = "DISPATCH";
++ events->values.push_back(object);
++ }
++
++ virtual void visit(const ExitedEvent& event)
++ {
++ JSON::Object object;
++ object.values["type"] = "EXITED";
++ events->values.push_back(object);
++ }
++
++ virtual void visit(const TerminateEvent& event)
++ {
++ JSON::Object object;
++ object.values["type"] = "TERMINATE";
++ events->values.push_back(object);
++ }
++
++ JSON::Array* events;
++ } visitor(&events);
++
++ foreach (Event* event, process->events) {
++ event->visit(&visitor);
++ }
++
++ object.values["events"] = events;
++ array.values.push_back(object);
++ }
++ }
++
++ return OK(array);
++}
++
++
++Timer Timer::create(
++ const Duration& duration,
++ const lambda::function<void(void)>& thunk)
++{
++ static uint64_t id = 1; // Start at 1 since Timer() instances use id 0.
++
++ // Assumes Clock::now() does Clock::now(__process__).
++ Timeout timeout = Timeout::in(duration);
++
++ UPID pid = __process__ != NULL ? __process__->self() : UPID();
++
++ Timer timer(__sync_fetch_and_add(&id, 1), timeout, pid, thunk);
++
++ VLOG(3) << "Created a timer for " << timeout.time();
++
++ // Add the timer.
++ synchronized (timeouts) {
++ if (timeouts->size() == 0 ||
++ timer.timeout().time() < timeouts->begin()->first) {
++ // Need to interrupt the loop to update/set timer repeat.
++ (*timeouts)[timer.timeout().time()].push_back(timer);
++ update_timer = true;
++ ev_async_send(loop, &async_watcher);
++ } else {
++ // Timer repeat is adequate, just add the timeout.
++ CHECK(timeouts->size() >= 1);
++ (*timeouts)[timer.timeout().time()].push_back(timer);
++ }
++ }
++
++ return timer;
++}
++
++
++bool Timer::cancel(const Timer& timer)
++{
++ bool canceled = false;
++ synchronized (timeouts) {
++ // Check if the timeout is still pending, and if so, erase it. In
++ // addition, erase an empty list if we just removed the last
++ // timeout.
++ Time time = timer.timeout().time();
++ if (timeouts->count(time) > 0) {
++ canceled = true;
++ (*timeouts)[time].remove(timer);
++ if ((*timeouts)[time].empty()) {
++ timeouts->erase(time);
++ }
++ }
++ }
++
++ return canceled;
++}
++
++
++ProcessBase::ProcessBase(const string& id)
++{
++ process::initialize();
++
++ state = ProcessBase::BOTTOM;
++
++ pthread_mutexattr_t attr;
++ pthread_mutexattr_init(&attr);
++ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
++ pthread_mutex_init(&m, &attr);
++ pthread_mutexattr_destroy(&attr);
++
++ refs = 0;
++
++ pid.id = id != "" ? id : ID::generate();
++ pid.ip = __ip__;
++ pid.port = __port__;
++
++ // If using a manual clock, try and set current time of process
++ // using happens before relationship between creator and createe!
++ if (Clock::paused()) {
++ synchronized (timeouts) {
++ if (Clock::paused()) {
++ clock::currents->erase(this); // In case the address is reused!
++ if (__process__ != NULL) {
++ Clock::order(__process__, this);
++ } else {
++ Clock::update(this, Clock::now());
++ }
++ }
++ }
++ }
++}
++
++
++ProcessBase::~ProcessBase() {}
++
++
++void ProcessBase::enqueue(Event* event, bool inject)
++{
++ CHECK(event != NULL);
++
++ lock();
++ {
++ if (state != TERMINATING && state != TERMINATED) {
++ if (!inject) {
++ events.push_back(event);
++ } else {
++ events.push_front(event);
++ }
++
++ if (state == BLOCKED) {
++ state = READY;
++ process_manager->enqueue(this);
++ }
++
++ CHECK(state == BOTTOM ||
++ state == READY ||
++ state == RUNNING);
++ } else {
++ delete event;
++ }
++ }
++ unlock();
++}
++
++
++void ProcessBase::inject(const UPID& from, const string& name, const char* data, size_t length)
++{
++ if (!from)
++ return;
++
++ Message* message = encode(from, pid, name, string(data, length));
++
++ enqueue(new MessageEvent(message), true);
++}
++
++
++void ProcessBase::send(const UPID& to, const string& name, const char* data, size_t length)
++{
++ if (!to) {
++ return;
++ }
++
++ // Encode and transport outgoing message.
++ transport(encode(pid, to, name, string(data, length)), this);
++}
++
++
++void ProcessBase::visit(const MessageEvent& event)
++{
++ if (handlers.message.count(event.message->name) > 0) {
++ handlers.message[event.message->name](
++ event.message->from,
++ event.message->body);
++ } else if (delegates.count(event.message->name) > 0) {
++ VLOG(1) << "Delegating message '" << event.message->name
++ << "' to " << delegates[event.message->name];
++ Message* message = new Message(*event.message);
++ message->to = delegates[event.message->name];
++ transport(message, this);
++ }
++}
++
++
++void ProcessBase::visit(const DispatchEvent& event)
++{
++ (*event.f)(this);
++}
++
++
++void ProcessBase::visit(const HttpEvent& event)
++{
++ VLOG(1) << "Handling HTTP event for process '" << pid.id << "'"
++ << " with path: '" << event.request->path << "'";
++
++ CHECK(event.request->path.find('/') == 0); // See ProcessManager::handle.
++
++ // Split the path by '/'.
++ vector<string> tokens = strings::tokenize(event.request->path, "/");
++ CHECK(tokens.size() >= 1);
++ CHECK(tokens[0] == pid.id);
++
++ const string& name = tokens.size() > 1 ? tokens[1] : "";
++
++ if (handlers.http.count(name) > 0) {
++ // Create the promise to link with whatever gets returned, as well
++ // as a future to wait for the response.
++ std::tr1::shared_ptr<Promise<Response> > promise(
++ new Promise<Response>());
++
++ Future<Response>* future = new Future<Response>(promise->future());
++
++ // Get the HttpProxy pid for this socket.
++ PID<HttpProxy> proxy = socket_manager->proxy(event.socket);
++
++ // Let the HttpProxy know about this request (via the future).
++ dispatch(proxy, &HttpProxy::handle, future, *event.request);
++
++ // Now call the handler and associate the response with the promise.
++ promise->associate(handlers.http[name](*event.request));
++ } else if (assets.count(name) > 0) {
++ OK response;
++ response.type = Response::PATH;
++ response.path = assets[name].path;
++
++ // Construct the final path by appending remaining tokens.
++ for (int i = 2; i < tokens.size(); i++) {
++ response.path += "/" + tokens[i];
++ }
++
++ // Try and determine the Content-Type from an extension.
++ Try<string> basename = os::basename(response.path);
++ if (!basename.isError()) {
++ size_t index = basename.get().find_last_of('.');
++ if (index != string::npos) {
++ string extension = basename.get().substr(index);
++ if (assets[name].types.count(extension) > 0) {
++ response.headers["Content-Type"] = assets[name].types[extension];
++ }
++ }
++ }
++
++ // TODO(benh): Use "text/plain" for assets that don't have an
++ // extension or we don't have a mapping for? It might be better to
++ // just let the browser guess (or do it's own default).
++
++ // Get the HttpProxy pid for this socket.
++ PID<HttpProxy> proxy = socket_manager->proxy(event.socket);
++
++ // Enqueue the response with the HttpProxy so that it respects the
++ // order of requests to account for HTTP/1.1 pipelining.
++ dispatch(proxy, &HttpProxy::enqueue, response, *event.request);
++ } else {
++ VLOG(1) << "Returning '404 Not Found' for '" << event.request->path << "'";
++
++ // Get the HttpProxy pid for this socket.
++ PID<HttpProxy> proxy = socket_manager->proxy(event.socket);
++
++ // Enqueue the response with the HttpProxy so that it respects the
++ // order of requests to account for HTTP/1.1 pipelining.
++ dispatch(proxy, &HttpProxy::enqueue, NotFound(), *event.request);
++ }
++}
++
++
++void ProcessBase::visit(const ExitedEvent& event)
++{
++ exited(event.pid);
++}
++
++
++void ProcessBase::visit(const TerminateEvent& event)
++{
++ finalize();
++}
++
++
++UPID ProcessBase::link(const UPID& to)
++{
++ if (!to) {
++ return to;
++ }
++
++ process_manager->link(this, to);
++
++ return to;
++}
++
++
++bool ProcessBase::route(
++ const string& name,
++ const Option<string>& help_,
++ const HttpRequestHandler& handler)
++{
++ if (name.find('/') != 0) {
++ return false;
++ }
++ handlers.http[name.substr(1)] = handler;
++ dispatch(help, &Help::add, pid.id, name, help_);
++ return true;
++}
++
++
++
++UPID spawn(ProcessBase* process, bool manage)
++{
++ process::initialize();
++
++ if (process != NULL) {
++ // If using a manual clock, try and set current time of process
++ // using happens before relationship between spawner and spawnee!
++ if (Clock::paused()) {
++ synchronized (timeouts) {
++ if (Clock::paused()) {
++ if (__process__ != NULL) {
++ Clock::order(__process__, process);
++ } else {
++ Clock::update(process, Clock::now());
++ }
++ }
++ }
++ }
++
++ return process_manager->spawn(process, manage);
++ } else {
++ return UPID();
++ }
++}
++
++
++void terminate(const UPID& pid, bool inject)
++{
++ process_manager->terminate(pid, inject, __process__);
++}
++
++
++class WaitWaiter : public Process<WaitWaiter>
++{
++public:
++ WaitWaiter(const UPID& _pid, const Duration& _duration, bool* _waited)
++ : ProcessBase(ID::generate("__waiter__")),
++ pid(_pid),
++ duration(_duration),
++ waited(_waited) {}
++
++ virtual void initialize()
++ {
++ VLOG(3) << "Running waiter process for " << pid;
++ link(pid);
++ delay(duration, self(), &WaitWaiter::timeout);
++ }
++
++private:
++ virtual void exited(const UPID&)
++ {
++ VLOG(3) << "Waiter process waited for " << pid;
++ *waited = true;
++ terminate(self());
++ }
++
++ void timeout()
++ {
++ VLOG(3) << "Waiter process timed out waiting for " << pid;
++ *waited = false;
++ terminate(self());
++ }
++
++private:
++ const UPID pid;
++ const Duration duration;
++ bool* const waited;
++};
++
++
++bool wait(const UPID& pid, const Duration& duration)
++{
++ process::initialize();
++
++ if (!pid) {
++ return false;
++ }
++
++ // This could result in a deadlock if some code decides to wait on a
++ // process that has invoked that code!
++ if (__process__ != NULL && __process__->self() == pid) {
++ std::cerr << "\n**** DEADLOCK DETECTED! ****\nYou are waiting on process "
++ << pid << " that it is currently executing." << std::endl;
++ }
++
++ if (duration == Seconds(-1)) {
++ return process_manager->wait(pid);
++ }
++
++ bool waited = false;
++
++ WaitWaiter waiter(pid, duration, &waited);
++ spawn(waiter);
++ wait(waiter);
++
++ return waited;
++}
++
++
++void filter(Filter *filter)
++{
++ process::initialize();
++
++ synchronized (filterer) {
++ filterer = filter;
++ }
++}
++
++
++void post(const UPID& to, const string& name, const char* data, size_t length)
++{
++ process::initialize();
++
++ if (!to) {
++ return;
++ }
++
++ // Encode and transport outgoing message.
++ transport(encode(UPID(), to, name, string(data, length)));
++}
++
++
++void post(const UPID& from,
++ const UPID& to,
++ const string& name,
++ const char* data,
++ size_t length)
++{
++ process::initialize();
++
++ if (!to) {
++ return;
++ }
++
++ // Encode and transport outgoing message.
++ transport(encode(from, to, name, string(data, length)));
++}
++
++
++namespace io {
++
++namespace internal {
++
++void read(int fd,
++ void* data,
++ size_t size,
++ const std::tr1::shared_ptr<Promise<size_t> >& promise,
++ const Future<short>& future)
++{
++ // Ignore this function if the read operation has been cancelled.
++ if (promise->future().isDiscarded()) {
++ return;
++ }
++
++ // Since promise->future() will be discarded before future is
++ // discarded, we should never see a discarded future here because of
++ // the check in the beginning of this function.
++ CHECK(!future.isDiscarded());
++
++ if (future.isFailed()) {
++ promise->fail(future.failure());
++ } else {
++ ssize_t length = ::read(fd, data, size);
++ if (length < 0) {
++ if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
++ // Restart the read operation.
++ poll(fd, process::io::READ).onAny(
++ lambda::bind(&internal::read, fd, data, size, promise, lambda::_1));
++ } else {
++ // Error occurred.
++ promise->fail(strerror(errno));
++ }
++ } else {
++ promise->set(length);
++ }
++ }
++}
++
++} // namespace internal {
++
++
++Future<short> poll(int fd, short events)
++{
++ process::initialize();
++
++ // TODO(benh): Check if the file descriptor is non-blocking?
++
++ Promise<short>* promise = new Promise<short>();
++
++ // Get a copy of the future to avoid any races with the event loop.
++ Future<short> future = promise->future();
++
++ ev_io* watcher = new ev_io();
++ watcher->data = promise;
++
++ ev_io_init(watcher, polled, fd, events);
++
++ // Enqueue the watcher.
++ synchronized (watchers) {
++ watchers->push(watcher);
++ }
++
++ // Interrupt the loop.
++ ev_async_send(loop, &async_watcher);
++
++ return future;
++}
++
++
++Future<size_t> read(int fd, void* data, size_t size)
++{
++ process::initialize();
++
++ std::tr1::shared_ptr<Promise<size_t> > promise(new Promise<size_t>());
++
++ // Check the file descriptor.
++ Try<bool> nonblock = os::isNonblock(fd);
++ if (nonblock.isError()) {
++ // The file descriptor is not valid (e.g. fd has been closed).
++ promise->fail(string("Failed to check O_NONBLOCK") + strerror(errno));
++ return promise->future();
++ } else if (!nonblock.get()) {
++ // The fd is not opened with O_NONBLOCK set.
++ promise->fail("Please use a fd opened with O_NONBLOCK set");
++ return promise->future();
++ }
++
++ if (size == 0) {
++ promise->fail("Try to read nothing");
++ return promise->future();
++ }
++
++ // Because the file descriptor is non-blocking, we call read()
++ // immediately. The read may in turn call poll if needed, avoiding
++ // unnecessary polling. We also observed that for some combination
++ // of libev and Linux kernel versions, the poll would block for
++ // non-deterministically long periods of time. This may be fixed in
++ // a newer version of libev (we use 3.8 at the time of writing this
++ // comment).
++ internal::read(fd, data, size, promise, io::READ);
++
++ return promise->future();
++}
++
++namespace internal {
++
++#if __cplusplus >= 201103L
++Future<string> _read(int fd,
++ const std::tr1::shared_ptr<string>& buffer,
++ const boost::shared_array<char>& data,
++ size_t length)
++{
++ return io::read(fd, data.get(), length)
++ .then([=] (size_t size) {
++ if (size == 0) { // EOF.
++ return string(*buffer);
++ }
++ buffer->append(data, size);
++ return _read(fd, buffer, data, length);
++ });
++}
++#else
++// Forward declataion.
++Future<string> _read(int fd,
++ const std::tr1::shared_ptr<string>& buffer,
++ const boost::shared_array<char>& data,
++ size_t length);
++
++
++Future<string> __read(
++ const size_t& size,
++ // TODO(benh): Remove 'const &' after fixing libprocess.
++ int fd,
++ const std::tr1::shared_ptr<string>& buffer,
++ const boost::shared_array<char>& data,
++ size_t length)
++{
++ if (size == 0) { // EOF.
++ return string(*buffer);
++ }
++
++ buffer->append(data.get(), size);
++
++ return _read(fd, buffer, data, length);
++}
++
++
++Future<string> _read(int fd,
++ const std::tr1::shared_ptr<string>& buffer,
++ const boost::shared_array<char>& data,
++ size_t length)
++{
++ return io::read(fd, data.get(), length)
++ .then(lambda::bind(&__read, lambda::_1, fd, buffer, data, length));
++}
++#endif
++
++} // namespace internal
++
++
++Future<string> read(int fd)
++{
++ process::initialize();
++
++ // TODO(benh): Wrap up this data as a struct, use 'Owner'.
++ // TODO(bmahler): For efficiency, use a rope for the buffer.
++ std::tr1::shared_ptr<string> buffer(new string());
++ boost::shared_array<char> data(new char[BUFFERED_READ_SIZE]);
++
++ return internal::_read(fd, buffer, data, BUFFERED_READ_SIZE);
++}
++
++
++} // namespace io {
++
++
++namespace http {
++
++namespace internal {
++
++Future<Response> decode(const string& buffer)
++{
++ ResponseDecoder decoder;
++ deque<Response*> responses = decoder.decode(buffer.c_str(), buffer.length());
++
++ if (decoder.failed() || responses.empty()) {
++ for (size_t i = 0; i < responses.size(); ++i) {
++ delete responses[i];
++ }
++ return Failure("Failed to decode HTTP response:\n" + buffer + "\n");
++ } else if (responses.size() > 1) {
++ PLOG(ERROR) << "Received more than 1 HTTP Response";
++ }
++
++ Response response = *responses[0];
++ for (size_t i = 0; i < responses.size(); ++i) {
++ delete responses[i];
++ }
++
++ return response;
++}
++
++} // namespace internal {
++
++
++Future<Response> get(const UPID& upid, const string& path, const string& query)
++{
++ int s = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
++
++ if (s < 0) {
++ return Failure(string("Failed to create socket: ") + strerror(errno));
++ }
++
++ Try<Nothing> cloexec = os::cloexec(s);
++ if (!cloexec.isSome()) {
++ return Failure("Failed to cloexec: " + cloexec.error());
++ }
++
++ sockaddr_in addr;
++ memset(&addr, 0, sizeof(addr));
++ addr.sin_family = AF_INET;
++ addr.sin_port = htons(upid.port);
++ addr.sin_addr.s_addr = upid.ip;
++
++ if (connect(s, (sockaddr*) &addr, sizeof(addr)) < 0) {
++ os::close(s);
++ return Failure(string("Failed to connect: ") + strerror(errno));
++ }
++
++ std::ostringstream out;
++
++ if (query.empty()) {
++ out << "GET /" << upid.id << "/" << path << " HTTP/1.1\r\n";
++ } else {
++ out << "GET /" << upid.id << "/" << path << "?" << query << " HTTP/1.1\r\n";
++ }
++
++ // Call inet_ntop since inet_ntoa is not thread-safe!
++ char ip[INET_ADDRSTRLEN];
++ PCHECK(inet_ntop(AF_INET, (in_addr *) &upid.ip, ip, INET_ADDRSTRLEN) != NULL);
++
++ out << "Host: " << ip << ":" << upid.port << "\r\n"
++ << "Connection: close\r\n"
++ << "\r\n";
++
++ // TODO(bmahler): Use benh's async write when it gets committed.
++ const string& data = out.str();
++ int remaining = data.size();
++
++ while (remaining > 0) {
++ int n = write(s, data.data() + (data.size() - remaining), remaining);
++
++ if (n < 0) {
++ if (errno == EINTR) {
++ continue;
++ }
++ os::close(s);
++ return Failure(string("Failed to write: ") + strerror(errno));
++ }
++
++ remaining -= n;
++ }
++
++ Try<Nothing> nonblock = os::nonblock(s);
++ if (!nonblock.isSome()) {
++ os::close(s);
++ return Failure("Failed to set nonblock: " + nonblock.error());
++ }
++
++ // Decode once the async read completes.
++ return io::read(s)
++ .then(lambda::bind(&internal::decode, lambda::_1))
++ .onAny(lambda::bind(&os::close, s));
++}
++
++} // namespace http {
++
++namespace internal {
++
++void dispatch(
++ const UPID& pid,
++ const std::tr1::shared_ptr<std::tr1::function<void(ProcessBase*)> >& f,
++ const string& method)
++{
++ process::initialize();
++
++ DispatchEvent* event = new DispatchEvent(pid, f, method);
++ process_manager->deliver(pid, event, __process__);
++}
++
++} // namespace internal {
++} // namespace process {
+diff --git a/src/libprocess/statistics.cpp b/src/libprocess/statistics.cpp
+new file mode 100644
+index 0000000..d4ba9f1
+--- /dev/null
++++ b/src/libprocess/statistics.cpp
+@@ -0,0 +1,538 @@
++#include <glog/logging.h>
++
++#include <algorithm>
++#include <list>
++#include <map>
++#include <string>
++#include <utility>
++#include <vector>
++
++#include <process/clock.hpp>
++#include <process/delay.hpp>
++#include <process/dispatch.hpp>
++#include <process/future.hpp>
++#include <process/help.hpp>
++#include <process/http.hpp>
++#include <process/process.hpp>
++#include <process/statistics.hpp>
++#include <process/time.hpp>
++
++#include <stout/error.hpp>
++#include <stout/duration.hpp>
++#include <stout/foreach.hpp>
++#include <stout/hashmap.hpp>
++#include <stout/hashset.hpp>
++#include <stout/json.hpp>
++#include <stout/none.hpp>
++#include <stout/numify.hpp>
++#include <stout/option.hpp>
++#include <stout/stringify.hpp>
++#include <stout/strings.hpp>
++
++using namespace process;
++using namespace process::http;
++
++using std::list;
++using std::map;
++using std::string;
++using std::vector;
++
++namespace process {
++
++// This is initialized by process::initialize().
++Statistics* statistics = NULL;
++
++// TODO(bmahler): Move time series related logic into this struct.
++// TODO(bmahler): Investigate using google's btree implementation.
++// This provides better insertion and lookup performance for large
++// containers. This _should_ also provide significant memory
++// savings, especially since:
++// 1. Our insertion order will mostly be in sorted order.
++// 2. Our keys (Seconds) have efficient comparison operators.
++// See: http://code.google.com/p/cpp-btree/
++// http://code.google.com/p/cpp-btree/wiki/UsageInstructions
++struct TimeSeries
++{
++ TimeSeries() : values(), archived(false) {}
++
++ // We use a map instead of a hashmap to store the values because
++ // that way we can retrieve a series in sorted order efficiently.
++ map<Time, double> values;
++ bool archived;
++};
++
++
++class StatisticsProcess : public Process<StatisticsProcess>
++{
++public:
++ StatisticsProcess(const Duration& _window)
++ : ProcessBase("statistics"),
++ window(_window) {}
++
++ virtual ~StatisticsProcess() {}
++
++ // Statistics implementation.
++ map<Time, double> timeseries(
++ const string& context,
++ const string& name,
++ const Option<Time>& start,
++ const Option<Time>& stop);
++
++ Option<double> get(const string& context, const string& name);
++
++ map<string, double> get(const string& context);
++
++ Try<Nothing> meter(
++ const string& context,
++ const string& name,
++ const Owned<meters::Meter>& meter);
++
++ void set(
++ const string& context,
++ const string& name,
++ double value,
++ const Time& time);
++
++ void archive(const string& context, const string& name);
++
++ void increment(const string& context, const string& name);
++
++ void decrement(const string& context, const string& name);
++
++protected:
++ virtual void initialize()
++ {
++ route("/snapshot.json", SNAPSHOT_HELP, &StatisticsProcess::snapshot);
++ route("/series.json", SERIES_HELP, &StatisticsProcess::series);
++
++ // Schedule the first truncation.
++ delay(STATISTICS_TRUNCATION_INTERVAL, self(), &StatisticsProcess::truncate);
++ }
++
++private:
++ static const string SNAPSHOT_HELP;
++ static const string SERIES_HELP;
++
++ // Removes values for the specified statistic that occurred outside
++ // the time series window.
++ // NOTE: We always ensure there is at least 1 value left for a statistic,
++ // unless it is archived!
++ // Returns true iff the time series is empty.
++ bool truncate(const string& context, const string& name);
++
++ // Removes values for all statistics that occurred outside the time
++ // series window.
++ // NOTE: Runs periodically every STATISTICS_TRUNCATION_INTERVAL.
++ // NOTE: We always ensure there is at least 1 value left for a statistic,
++ // unless it is archived.
++ void truncate();
++
++ // Returns the a snapshot of all statistics in JSON.
++ Future<Response> snapshot(const Request& request);
++
++ // Returns the time series of a statistic in JSON.
++ Future<Response> series(const Request& request);
++
++ const Duration window;
++
++ // This maps from {context: {name: TimeSeries } }.
++ hashmap<string, hashmap<string, TimeSeries> > statistics;
++
++ // Each statistic can have many meters.
++ // This maps from {context: {name: [meters] } }.
++ hashmap<string, hashmap<string, list<Owned<meters::Meter> > > > meters;
++};
++
++
++const string StatisticsProcess::SERIES_HELP = HELP(
++ TLDR(
++ "Provides the time series for ..."),
++ USAGE(
++ "/statistics/series.json..."),
++ DESCRIPTION(
++ "...",
++ "",
++ "Query parameters:",
++ "",
++ "> param=VALUE Some description here"));
++
++
++const string StatisticsProcess::SNAPSHOT_HELP = HELP(
++ TLDR(
++ "Provides a snapshot of the current statistics ..."),
++ USAGE(
++ "/statistics/snapshot.json..."),
++ DESCRIPTION(
++ "...",
++ "",
++ "Query parameters:",
++ "",
++ "> param=VALUE Some description here"));
++
++
++Try<Nothing> StatisticsProcess::meter(
++ const string& context,
++ const string& name,
++ const Owned<meters::Meter>& meter)
++{
++ if (meter->name == name) {
++ return Error("Meter name must not match the statistic name");
++ }
++
++ // Check for a duplicate meter.
++ foreachkey (const string& context, meters) {
++ foreachkey (const string& name, meters[context]) {
++ foreach (Owned<meters::Meter>& existing, meters[context][name]) {
++ if (meter->name == existing->name) {
++ return Error("Meter name matched existing meter name");
++ }
++ }
++ }
++ }
++
++ // Add the meter.
++ meters[context][name].push_back(meter);
++
++ return Nothing();
++}
++
++
++map<Time, double> StatisticsProcess::timeseries(
++ const string& context,
++ const string& name,
++ const Option<Time>& start,
++ const Option<Time>& stop)
++{
++ if (!statistics.contains(context) || !statistics[context].contains(name)) {
++ return map<Time, double>();
++ }
++
++ const std::map<Time, double>& values =
++ statistics[context].find(name)->second.values;
++
++ map<Time, double>::const_iterator lower = values.lower_bound(start.isSome()
++ ? start.get() : Time::EPOCH);
++
++ map<Time, double>::const_iterator upper = values.upper_bound(stop.isSome()
++ ? stop.get() : Time::MAX);
++
++ return map<Time, double>(lower, upper);
++}
++
++
++Option<double> StatisticsProcess::get(const string& context, const string& name)
++{
++ if (!statistics.contains(context) ||
++ !statistics[context].contains(name) ||
++ statistics[context][name].values.empty()) {
++ return Option<double>::none();
++ } else {
++ return statistics[context][name].values.rbegin()->second;
++ }
++}
++
++
++map<string, double> StatisticsProcess::get(const string& context)
++{
++ map<string, double> results;
++
++ if (!statistics.contains(context)) {
++ return results;
++ }
++
++ foreachkey (const string& name, statistics[context]) {
++ const map<Time, double>& values = statistics[context][name].values;
++
++ if (!values.empty()) {
++ results[name] = values.rbegin()->second;
++ }
++ }
++
++ return results;
++}
++
++
++void StatisticsProcess::set(
++ const string& context,
++ const string& name,
++ double value,
++ const Time& time)
++{
++ statistics[context][name].values[time] = value; // Update the raw value.
++ statistics[context][name].archived = false; // Unarchive.
++
++ truncate(context, name);
++
++ // Update the metered values, if necessary.
++ if (meters.contains(context) && meters[context].contains(name)) {
++ foreach (Owned<meters::Meter>& meter, meters[context][name]) {
++ const Option<double>& update = meter->update(time, value);
++ statistics[context][meter->name].archived = false; // Unarchive.
++
++ if (update.isSome()) {
++ statistics[context][meter->name].values[time] = update.get();
++ truncate(context, meter->name);
++ }
++ }
++ }
++}
++
++
++void StatisticsProcess::archive(const string& context, const string& name)
++{
++ // Exclude the statistic from the snapshot.
++ statistics[context][name].archived = true;
++
++ // Remove any meters as well.
++ if (meters.contains(context) && meters[context].contains(name)) {
++ foreach (const Owned<meters::Meter>& meter, meters[context][name]) {
++ statistics[context][meter->name].archived = true;
++ }
++ meters[context].erase(name);
++ }
++}
++
++
++void StatisticsProcess::increment(const string& context, const string& name)
++{
++ double value = 0.0;
++ if (!statistics[context][name].values.empty()) {
++ value = statistics[context][name].values.rbegin()->second;
++ }
++ set(context, name, value + 1.0, Clock::now());
++}
++
++
++void StatisticsProcess::decrement(const string& context, const string& name)
++{
++ double value = 0.0;
++ if (!statistics[context][name].values.empty()) {
++ value = statistics[context][name].values.rbegin()->second;
++ }
++ set(context, name, value - 1.0, Clock::now());
++}
++
++
++bool StatisticsProcess::truncate(const string& context, const string& name)
++{
++ CHECK(statistics.contains(context));
++ CHECK(statistics[context].contains(name));
++
++ if (statistics[context][name].values.empty()) {
++ return true; // No truncation is needed, the time series is already empty.
++ }
++
++ map<Time, double>::iterator start =
++ statistics[context][name].values.begin();
++
++ while ((Clock::now() - start->first) > window) {
++ // Always keep at least one value for a statistic, unless it's archived!
++ if (statistics[context][name].values.size() == 1) {
++ if (statistics[context][name].archived) {
++ statistics[context][name].values.clear();
++ }
++ break;
++ }
++
++ statistics[context][name].values.erase(start);
++ start = statistics[context][name].values.begin();
++ }
++
++ return statistics[context][name].values.empty();
++}
++
++
++void StatisticsProcess::truncate()
++{
++ hashmap<string, hashset<string> > empties;
++
++ foreachkey (const string& context, statistics) {
++ foreachkey (const string& name, statistics[context]) {
++ // Keep track of the emptied timeseries.
++ if (truncate(context, name)) {
++ empties[context].insert(name);
++ }
++ }
++ }
++
++ // Remove the empty timeseries.
++ foreachkey (const string& context, empties) {
++ foreach (const string& name, empties[context]) {
++ statistics[context].erase(name);
++ }
++ }
++
++ delay(STATISTICS_TRUNCATION_INTERVAL, self(), &StatisticsProcess::truncate);
++}
++
++
++Future<Response> StatisticsProcess::snapshot(const Request& request)
++{
++ JSON::Array array;
++
++ Option<string> queryContext = request.query.get("context");
++ Option<string> queryName = request.query.get("name");
++
++ foreachkey (const string& context, statistics) {
++ foreachkey (const string& name, statistics[context]) {
++ // Exclude archived and empty time series.
++ if (statistics[context][name].archived ||
++ statistics[context][name].values.empty()) {
++ continue;
++ }
++
++ // Skip statistics that don't match the query, if present.
++ if (queryContext.isSome() && queryContext.get() != context) {
++ continue;
++ } else if (queryName.isSome() && queryName.get() != name) {
++ continue;
++ }
++
++ JSON::Object object;
++ object.values["context"] = context;
++ object.values["name"] = name;
++ object.values["time"] =
++ statistics[context][name].values.rbegin()->first.secs();
++ object.values["value"] =
++ statistics[context][name].values.rbegin()->second;
++ array.values.push_back(object);
++ }
++ }
++
++ return OK(array, request.query.get("jsonp"));
++}
++
++
++Future<Response> StatisticsProcess::series(const Request& request)
++{
++ Option<string> context = request.query.get("context");
++ Option<string> name = request.query.get("name");
++
++ if (!context.isSome()) {
++ return BadRequest("Expected 'context=val' in query.\n");
++ } else if (!name.isSome()) {
++ return BadRequest("Expected 'name=val' in query.\n");
++ }
++
++ Option<Time> start = None();
++ Option<Time> stop = None();
++
++ if (request.query.get("start").isSome()) {
++ Try<double> result = numify<double>(request.query.get("start").get());
++ if (result.isError()) {
++ return BadRequest("Failed to parse 'start': " + result.error());
++ }
++
++ Try<Time> start_ = Time::create(result.get());
++ if (start_.isError()) {
++ return BadRequest("Failed to parse 'start': " + start_.error());
++ }
++ start = start_.get();
++ }
++
++ if (request.query.get("stop").isSome()) {
++ Try<double> result = numify<double>(request.query.get("stop").get());
++ if (result.isError()) {
++ return BadRequest("Failed to parse 'stop': " + result.error());
++ }
++
++ Try<Time> stop_ = Time::create(result.get());
++ if (stop_.isError()) {
++ return BadRequest("Failed to parse 'stop': " + stop_.error());
++ }
++ stop = stop_.get();
++ }
++
++ JSON::Array array;
++
++ const map<Time, double>& values =
++ timeseries(context.get(), name.get(), start, stop);
++
++ foreachpair (const Time& s, double value, values) {
++ JSON::Object object;
++ object.values["time"] = s.secs();
++ object.values["value"] = value;
++ array.values.push_back(object);
++ }
++
++ return OK(array, request.query.get("jsonp"));
++}
++
++
++Statistics::Statistics(const Duration& window)
++{
++ process = new StatisticsProcess(window);
++ spawn(process);
++}
++
++
++Statistics::~Statistics()
++{
++ terminate(process);
++ wait(process);
++}
++
++
++Future<map<Time, double> > Statistics::timeseries(
++ const string& context,
++ const string& name,
++ const Option<Time>& start,
++ const Option<Time>& stop)
++{
++ return dispatch(
++ process, &StatisticsProcess::timeseries, context, name, start, stop);
++}
++
++
++Future<Option<double> > Statistics::get(
++ const string& context,
++ const string& name)
++{
++ return dispatch(process, &StatisticsProcess::get, context, name);
++}
++
++
++Future<map<string, double> > Statistics::get(const string& context)
++{
++ return dispatch(process, &StatisticsProcess::get, context);
++}
++
++
++Future<Try<Nothing> > Statistics::meter(
++ const string& context,
++ const string& name,
++ Owned<meters::Meter> meter)
++{
++
++ return dispatch(process, &StatisticsProcess::meter, context, name, meter);
++}
++
++
++void Statistics::set(
++ const string& context,
++ const string& name,
++ double value,
++ const Time& time)
++{
++ dispatch(process, &StatisticsProcess::set, context, name, value, time);
++}
++
++
++void Statistics::archive(const string& context, const string& name)
++{
++ dispatch(process, &StatisticsProcess::archive, context, name);
++}
++
++
++void Statistics::increment(const string& context, const string& name)
++{
++ dispatch(process, &StatisticsProcess::increment, context, name);
++}
++
++
++void Statistics::decrement(const string& context, const string& name)
++{
++ dispatch(process, &StatisticsProcess::decrement, context, name);
++}
++
++} // namespace process {
+diff --git a/src/libprocess/synchronized.cpp b/src/libprocess/synchronized.cpp
+new file mode 100644
+index 0000000..79b0849
+--- /dev/null
++++ b/src/libprocess/synchronized.cpp
+@@ -0,0 +1,66 @@
++#include "synchronized.hpp"
++
++using std::string;
++
++
++static string s1;
++static synchronizable(s1);
++
++static string s2;
++static synchronizable(s2) = SYNCHRONIZED_INITIALIZER;
++
++static string s3;
++static synchronizable(s3) = SYNCHRONIZED_INITIALIZER_RECURSIVE;
++
++
++void bar()
++{
++ synchronized(s3) {
++
++ }
++}
++
++
++void foo()
++{
++ synchronized(s3) {
++ bar();
++ }
++}
++
++
++class Foo
++{
++public:
++ Foo()
++ {
++ synchronizer(s) = SYNCHRONIZED_INITIALIZER_RECURSIVE;
++ }
++
++ void foo()
++ {
++ synchronized(s) {
++ synchronized(s) {
++
++ }
++ }
++ }
++
++private:
++ string s;
++ synchronizable(s);
++};
++
++
++int main(int argc, char **argv)
++{
++ synchronizer(s1) = SYNCHRONIZED_INITIALIZER_RECURSIVE;
++ //synchronizer(s2) = SYNCHRONIZED_INITIALIZER;
++
++ //foo();
++
++ Foo f;
++ f.foo();
++
++ return 0;
++}
+diff --git a/src/libprocess/synchronized.hpp b/src/libprocess/synchronized.hpp
+new file mode 100644
+index 0000000..7e0efe2
+--- /dev/null
++++ b/src/libprocess/synchronized.hpp
+@@ -0,0 +1,104 @@
++#include <pthread.h>
++
++#include <iostream>
++
++
++class Synchronizable
++{
++public:
++ Synchronizable()
++ : initialized(false) {}
++
++ explicit Synchronizable(int _type)
++ : type(_type), initialized(false)
++ {
++ initialize();
++ }
++
++ Synchronizable(const Synchronizable &that)
++ {
++ type = that.type;
++ initialize();
++ }
++
++ Synchronizable & operator = (const Synchronizable &that)
++ {
++ type = that.type;
++ initialize();
++ return *this;
++ }
++
++ void acquire()
++ {
++ if (!initialized) {
++ std::cerr << "synchronizable not initialized" << std::endl;
++ abort();
++ }
++ pthread_mutex_lock(&mutex);
++ }
++
++ void release()
++ {
++ if (!initialized) {
++ std::cerr << "synchronizable not initialized" << std::endl;
++ abort();
++ }
++ pthread_mutex_unlock(&mutex);
++ }
++
++private:
++ void initialize()
++ {
++ if (!initialized) {
++ pthread_mutexattr_t attr;
++ pthread_mutexattr_init(&attr);
++ pthread_mutexattr_settype(&attr, type);
++ pthread_mutex_init(&mutex, &attr);
++ pthread_mutexattr_destroy(&attr);
++ initialized = true;
++ } else {
++ std::cerr << "synchronizable already initialized" << std::endl;
++ abort();
++ }
++ }
++
++ int type;
++ bool initialized;
++ pthread_mutex_t mutex;
++};
++
++
++class Synchronized
++{
++public:
++ Synchronized(Synchronizable *_synchronizable)
++ : synchronizable(_synchronizable)
++ {
++ synchronizable->acquire();
++ }
++
++ ~Synchronized()
++ {
++ synchronizable->release();
++ }
++
++ operator bool () { return true; }
++
++private:
++ Synchronizable *synchronizable;
++};
++
++
++#define synchronized(s) \
++ if (Synchronized __synchronized ## s = Synchronized(&__synchronizable_ ## s))
++
++#define synchronizable(s) \
++ Synchronizable __synchronizable_ ## s
++
++#define synchronizer(s) \
++ (__synchronizable_ ## s)
++
++
++#define SYNCHRONIZED_INITIALIZER Synchronizable(PTHREAD_MUTEX_NORMAL)
++#define SYNCHRONIZED_INITIALIZER_DEBUG Synchronizable(PTHREAD_MUTEX_ERRORCHECK)
++#define SYNCHRONIZED_INITIALIZER_RECURSIVE Synchronizable(PTHREAD_MUTEX_RECURSIVE)
+diff --git a/src/libprocess/test-master.cpp b/src/libprocess/test-master.cpp
+new file mode 100644
+index 0000000..f272919
+--- /dev/null
++++ b/src/libprocess/test-master.cpp
+@@ -0,0 +1,62 @@
++#include <io.hpp>
++#include <tuple.hpp>
++
++#include <string>
++
++#include "test.hpp"
++
++using std::string;
++
++
++using namespace process::tuple;
++
++
++class Master : public Tuple<Process>
++{
++private:
++ int id;
++
++protected:
++ void operator () ()
++ {
++ do {
++ switch (receive()) {
++ case REGISTER: {
++ Out::println("Master received REGISTER");
++
++ string name;
++ unpack<REGISTER>(name);
++
++ Out::println("Registered slave: %s", name.c_str());
++
++ send(from(), pack<OKAY>(id++));
++ break;
++ }
++ case UNREGISTER: {
++ Out::println("Master received UNREGISTER");
++
++ int slave_id;
++ unpack<UNREGISTER>(slave_id);
++
++ Out::println("Unregistered slave id: %d", slave_id);
++
++ send(from(), pack<OKAY>(0));
++ break;
++ }
++ default:
++ Out::println("UNKNOWN MESSAGE RECEIVED");
++ }
++ } while (true);
++ }
++
++public:
++ Master() : id(0) {}
++};
++
++
++int main(int argc, char **argv)
++{
++ PID master = Process::spawn(new Master());
++ Out::println("master: %s", string(master).c_str());
++ Process::wait(master);
++}
+diff --git a/src/libprocess/test-slave.cpp b/src/libprocess/test-slave.cpp
+new file mode 100644
+index 0000000..fe08ce8
+--- /dev/null
++++ b/src/libprocess/test-slave.cpp
+@@ -0,0 +1,61 @@
++#include <test.hpp>
++
++using namespace process::record;
++
++class Slave : public RecordProcess
++{
++private:
++ PID master;
++ int id;
++
++protected:
++ void operator () ()
++ {
++ send(master, pack<REGISTER>("c3po"));
++
++ switch (receive()) {
++ case OKAY: {
++ std::cout << "slave registered" << std::endl;
++ unpack<OKAY>(id);
++ std::cout << "slave id: " << id << std::endl;
++ break;
++ }
++ default:
++ std::cout << "slave failed to register" << std::endl;
++ break;
++ }
++
++ send(master, pack<UNREGISTER>(id));
++
++ switch (receive()) {
++ case OKAY:
++ std::cout << "slave unregistered" << std::endl;
++ break;
++ default:
++ std::cout << "slave failed to unregister" << std::endl;
++ break;
++ }
++
++ link(master);
++ switch (receive()) {
++ case PROCESS_EXIT:
++ std::cout << "master exited" << std::endl;
++ break;
++ default:
++ std::cout << "unexpected message" << std::endl;
++ break;
++ }
++ }
++
++public:
++ Slave(const PID &_master) : master(_master) {}
++};
++
++
++int main(int argc, char **argv)
++{
++ PID master = make_pid(argv[1]);
++ PID slave = Process::spawn(new Slave(master));
++ std::cout << "slave is at " << slave << std::endl;
++ Process::wait(slave);
++}
+diff --git a/src/libprocess/tests/decoder_tests.cpp b/src/libprocess/tests/decoder_tests.cpp
+new file mode 100644
+index 0000000..04ca3ff
+--- /dev/null
++++ b/src/libprocess/tests/decoder_tests.cpp
+@@ -0,0 +1,128 @@
++#include <gmock/gmock.h>
++
++#include <deque>
++#include <string>
++
++#include <process/socket.hpp>
++
++#include <stout/gtest.hpp>
++
++#include "decoder.hpp"
++
++using namespace process;
++using namespace process::http;
++
++using std::deque;
++using std::string;
++
++
++TEST(Decoder, Request)
++{
++ DataDecoder decoder = DataDecoder(Socket());
++
++ const string& data =
++ "GET /path/file.json?key1=value1&key2=value2#fragment HTTP/1.1\r\n"
++ "Host: localhost\r\n"
++ "Connection: close\r\n"
++ "Accept-Encoding: compress, gzip\r\n"
++ "\r\n";
++
++ deque<Request*> requests = decoder.decode(data.data(), data.length());
++ ASSERT_FALSE(decoder.failed());
++ ASSERT_EQ(1, requests.size());
++
++ Request* request = requests[0];
++ EXPECT_EQ("GET", request->method);
++ EXPECT_EQ("/path/file.json", request->path);
++ EXPECT_EQ("/path/file.json?key1=value1&key2=value2#fragment", request->url);
++ EXPECT_EQ("fragment", request->fragment);
++ EXPECT_TRUE(request->body.empty());
++ EXPECT_FALSE(request->keepAlive);
++
++ EXPECT_EQ(3, request->headers.size());
++ EXPECT_SOME_EQ("localhost", request->headers.get("Host"));
++ EXPECT_SOME_EQ("close", request->headers.get("Connection"));
++ EXPECT_SOME_EQ("compress, gzip", request->headers.get("Accept-Encoding"));
++
++ EXPECT_EQ(2, request->query.size());
++ EXPECT_SOME_EQ("value1", request->query.get("key1"));
++ EXPECT_SOME_EQ("value2", request->query.get("key2"));
++
++ delete request;
++}
++
++
++TEST(Decoder, RequestHeaderContinuation)
++{
++ DataDecoder decoder = DataDecoder(Socket());
++
++ const string& data =
++ "GET /path/file.json HTTP/1.1\r\n"
++ "Host: localhost\r\n"
++ "Connection: close\r\n"
++ "Accept-Encoding: compress,"
++ " gzip\r\n"
++ "\r\n";
++
++ deque<Request*> requests = decoder.decode(data.data(), data.length());
++ ASSERT_FALSE(decoder.failed());
++ ASSERT_EQ(1, requests.size());
++
++ Request* request = requests[0];
++ EXPECT_SOME_EQ("compress, gzip",
++ request->headers.get("Accept-Encoding"));
++ delete request;
++}
++
++
++// This is expected to fail for now, see my TODO(bmahler) on http::Request.
++TEST(Decoder, DISABLED_RequestHeaderCaseInsensitive)
++{
++ DataDecoder decoder = DataDecoder(Socket());
++
++ const string& data =
++ "GET /path/file.json HTTP/1.1\r\n"
++ "Host: localhost\r\n"
++ "cOnnECtioN: close\r\n"
++ "accept-ENCODING: compress, gzip\r\n"
++ "\r\n";
++
++ deque<Request*> requests = decoder.decode(data.data(), data.length());
++ ASSERT_FALSE(decoder.failed());
++ ASSERT_EQ(1, requests.size());
++
++ Request* request = requests[0];
++ EXPECT_FALSE(request->keepAlive);
++
++ EXPECT_SOME_EQ("compress, gzip", request->headers.get("Accept-Encoding"));
++
++ delete request;
++}
++
++
++TEST(Decoder, Response)
++{
++ ResponseDecoder decoder;
++
++ const string& data =
++ "HTTP/1.1 200 OK\r\n"
++ "Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n"
++ "Content-Type: text/plain\r\n"
++ "Content-Length: 2\r\n"
++ "\r\n"
++ "hi";
++
++ deque<Response*> requests = decoder.decode(data.data(), data.length());
++ ASSERT_FALSE(decoder.failed());
++ ASSERT_EQ(1, requests.size());
++
++ Response* response = requests[0];
++
++ EXPECT_EQ("200 OK", response->status);
++ EXPECT_EQ(Response::BODY, response->type);
++ EXPECT_EQ("hi", response->body);
++
++ EXPECT_EQ(3, response->headers.size());
++
++ delete response;
++}
+diff --git a/src/libprocess/tests/encoder_tests.cpp b/src/libprocess/tests/encoder_tests.cpp
+new file mode 100644
+index 0000000..fccb865
+--- /dev/null
++++ b/src/libprocess/tests/encoder_tests.cpp
+@@ -0,0 +1,92 @@
++#include <gmock/gmock.h>
++
++#include <deque>
++#include <string>
++#include <vector>
++
++#include <process/http.hpp>
++#include <process/socket.hpp>
++
++#include <stout/gtest.hpp>
++
++#include "encoder.hpp"
++#include "decoder.hpp"
++
++using namespace process;
++using namespace process::http;
++
++using std::deque;
++using std::string;
++using std::vector;
++
++
++TEST(Encoder, Response)
++{
++ Request request;
++ const OK& response("body");
++
++ // Encode the response.
++ const string& encoded = HttpResponseEncoder::encode(response, request);
++
++ // Now decode it back, and verify the encoding was correct.
++ ResponseDecoder decoder;
++ deque<Response*> responses = decoder.decode(encoded.data(), encoded.length());
++ ASSERT_FALSE(decoder.failed());
++ ASSERT_EQ(1, responses.size());
++
++ Response* decoded = responses[0];
++ EXPECT_EQ("200 OK", decoded->status);
++ EXPECT_EQ("body", decoded->body);
++
++ // Encoding should have inserted the 'Date' and 'Content-Length' headers.
++ EXPECT_EQ(2, decoded->headers.size());
++ EXPECT_TRUE(decoded->headers.contains("Date"));
++ EXPECT_SOME_EQ(
++ stringify(response.body.size()),
++ decoded->headers.get("Content-Length"));
++}
++
++
++TEST(Encoder, AcceptableEncodings)
++{
++ // Create requests that do not accept gzip encoding.
++ vector<Request> requests(7);
++ requests[0].headers["Accept-Encoding"] = "gzip;q=0.0,*";
++ requests[1].headers["Accept-Encoding"] = "compress";
++ requests[2].headers["Accept-Encoding"] = "compress, gzip;q=0.0";
++ requests[3].headers["Accept-Encoding"] = "*, gzip;q=0.0";
++ requests[4].headers["Accept-Encoding"] = "*;q=0.0, compress";
++ requests[5].headers["Accept-Encoding"] = "\n compress";
++ requests[6].headers["Accept-Encoding"] = "compress,\tgzip;q=0.0";
++
++ foreach (const Request& request, requests) {
++ EXPECT_FALSE(request.accepts("gzip"))
++ << "Gzip encoding is unacceptable for 'Accept-Encoding: "
++ << request.headers.get("Accept-Encoding").get() << "'";
++ }
++
++ // Create requests that accept gzip encoding.
++ vector<Request> gzipRequests(12);
++
++ // Using q values.
++ gzipRequests[0].headers["Accept-Encoding"] = "gzip;q=0.1,*";
++ gzipRequests[1].headers["Accept-Encoding"] = "compress, gzip;q=0.1";
++ gzipRequests[2].headers["Accept-Encoding"] = "*, gzip;q=0.5";
++ gzipRequests[3].headers["Accept-Encoding"] = "*;q=0.9, compress";
++ gzipRequests[4].headers["Accept-Encoding"] = "compress,\tgzip;q=0.1";
++
++ // No q values.
++ gzipRequests[5].headers["Accept-Encoding"] = "gzip";
++ gzipRequests[6].headers["Accept-Encoding"] = "compress, gzip";
++ gzipRequests[7].headers["Accept-Encoding"] = "*";
++ gzipRequests[8].headers["Accept-Encoding"] = "*, compress";
++ gzipRequests[9].headers["Accept-Encoding"] = "\n gzip";
++ gzipRequests[10].headers["Accept-Encoding"] = "compress,\tgzip";
++ gzipRequests[11].headers["Accept-Encoding"] = "gzip";
++
++ foreach (const Request& gzipRequest, gzipRequests) {
++ EXPECT_TRUE(gzipRequest.accepts("gzip"))
++ << "Gzip encoding is acceptable for 'Accept-Encoding: "
++ << gzipRequest.headers.get("Accept-Encoding").get() << "'";
++ }
++}
+diff --git a/src/libprocess/tests/http_tests.cpp b/src/libprocess/tests/http_tests.cpp
+new file mode 100644
+index 0000000..68d1a1b
+--- /dev/null
++++ b/src/libprocess/tests/http_tests.cpp
+@@ -0,0 +1,180 @@
++#include <arpa/inet.h>
++
++#include <gmock/gmock.h>
++
++#include <netinet/in.h>
++#include <netinet/tcp.h>
++
++#include <string>
++
++#include <process/future.hpp>
++#include <process/gmock.hpp>
++#include <process/gtest.hpp>
++#include <process/http.hpp>
++#include <process/io.hpp>
++
++#include <stout/gtest.hpp>
++#include <stout/none.hpp>
++#include <stout/nothing.hpp>
++#include <stout/os.hpp>
++
++#include "encoder.hpp"
++
++using namespace process;
++
++using testing::_;
++using testing::Assign;
++using testing::DoAll;
++using testing::Return;
++
++
++class HttpProcess : public Process<HttpProcess>
++{
++public:
++ HttpProcess()
++ {
++ route("/body", None(), &HttpProcess::body);
++ route("/pipe", None(), &HttpProcess::pipe);
++ }
++
++ MOCK_METHOD1(body, Future<http::Response>(const http::Request&));
++ MOCK_METHOD1(pipe, Future<http::Response>(const http::Request&));
++};
++
++
++TEST(HTTP, Endpoints)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ HttpProcess process;
++
++ spawn(process);
++
++ // First hit '/body' (using explicit sockets and HTTP/1.0).
++ int s = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
++
++ ASSERT_LE(0, s);
++
++ sockaddr_in addr;
++ memset(&addr, 0, sizeof(addr));
++ addr.sin_family = PF_INET;
++ addr.sin_port = htons(process.self().port);
++ addr.sin_addr.s_addr = process.self().ip;
++
++ ASSERT_EQ(0, connect(s, (sockaddr*) &addr, sizeof(addr)));
++
++ std::ostringstream out;
++ out << "GET /" << process.self().id << "/body"
++ << " HTTP/1.0\r\n"
++ << "Connection: Keep-Alive\r\n"
++ << "\r\n";
++
++ const std::string& data = out.str();
++
++ EXPECT_CALL(process, body(_))
++ .WillOnce(Return(http::OK()));
++
++ ASSERT_SOME(os::write(s, data));
++
++ std::string response = "HTTP/1.1 200 OK";
++
++ char temp[response.size()];
++ ASSERT_LT(0, ::read(s, temp, response.size()));
++ ASSERT_EQ(response, std::string(temp, response.size()));
++
++ ASSERT_EQ(0, close(s));
++
++ // Now hit '/pipe' (by using http::get).
++ int pipes[2];
++ ASSERT_NE(-1, ::pipe(pipes));
++
++ http::OK ok;
++ ok.type = http::Response::PIPE;
++ ok.pipe = pipes[0];
++
++ Future<Nothing> pipe;
++ EXPECT_CALL(process, pipe(_))
++ .WillOnce(DoAll(FutureSatisfy(&pipe),
++ Return(ok)));
++
++ Future<http::Response> future = http::get(process.self(), "pipe");
++
++ AWAIT_READY(pipe);
++
++ ASSERT_SOME(os::write(pipes[1], "Hello World\n"));
++ ASSERT_SOME(os::close(pipes[1]));
++
++ AWAIT_READY(future);
++ ASSERT_EQ(http::statuses[200], future.get().status);
++ ASSERT_EQ("chunked", future.get().headers["Transfer-Encoding"]);
++ ASSERT_EQ("Hello World\n", future.get().body);
++
++ terminate(process);
++ wait(process);
++}
++
++
++TEST(HTTP, Encode)
++{
++ std::string unencoded = "a$&+,/:;=?@ \"<>#%{}|\\^~[]`\x19\x80\xFF";
++ unencoded += std::string("\x00", 1); // Add a null byte to the end.
++
++ std::string encoded = http::encode(unencoded);
++
++ EXPECT_EQ("a%24%26%2B%2C%2F%3A%3B%3D%3F%40%20%22%3C%3E%23"
++ "%25%7B%7D%7C%5C%5E%7E%5B%5D%60%19%80%FF%00",
++ encoded);
++
++ EXPECT_SOME_EQ(unencoded, http::decode(encoded));
++
++ EXPECT_ERROR(http::decode("%"));
++ EXPECT_ERROR(http::decode("%1"));
++ EXPECT_ERROR(http::decode("%;1"));
++ EXPECT_ERROR(http::decode("%1;"));
++}
++
++
++TEST(HTTP, PathParse)
++{
++ const std::string pattern = "/books/{isbn}/chapters/{chapter}";
++
++ Try<hashmap<std::string, std::string> > parse =
++ http::path::parse(pattern, "/books/0304827484/chapters/3");
++
++ ASSERT_SOME(parse);
++ EXPECT_EQ(4, parse.get().size());
++ EXPECT_EQ("books", parse.get()["books"]);
++ EXPECT_EQ("0304827484", parse.get()["isbn"]);
++ EXPECT_EQ("chapters", parse.get()["chapters"]);
++ EXPECT_EQ("3", parse.get()["chapter"]);
++
++ parse = http::path::parse(pattern, "/books/0304827484");
++
++ ASSERT_SOME(parse);
++ EXPECT_EQ(2, parse.get().size());
++ EXPECT_EQ("books", parse.get()["books"]);
++ EXPECT_EQ("0304827484", parse.get()["isbn"]);
++
++ parse = http::path::parse(pattern, "/books/0304827484/chapters");
++
++ ASSERT_SOME(parse);
++ EXPECT_EQ(3, parse.get().size());
++ EXPECT_EQ("books", parse.get()["books"]);
++ EXPECT_EQ("0304827484", parse.get()["isbn"]);
++ EXPECT_EQ("chapters", parse.get()["chapters"]);
++
++ parse = http::path::parse(pattern, "/foo/0304827484/chapters");
++
++ EXPECT_ERROR(parse);
++ EXPECT_EQ("Expecting 'books' not 'foo'", parse.error());
++
++ parse = http::path::parse(pattern, "/books/0304827484/bar");
++
++ EXPECT_ERROR(parse);
++ EXPECT_EQ("Expecting 'chapters' not 'bar'", parse.error());
++
++ parse = http::path::parse(pattern, "/books/0304827484/chapters/3/foo/bar");
++
++ EXPECT_ERROR(parse);
++ EXPECT_EQ("Not expecting suffix 'foo/bar'", parse.error());
++}
+diff --git a/src/libprocess/tests/io_tests.cpp b/src/libprocess/tests/io_tests.cpp
+new file mode 100644
+index 0000000..ee5b0b4
+--- /dev/null
++++ b/src/libprocess/tests/io_tests.cpp
+@@ -0,0 +1,164 @@
++#include <gmock/gmock.h>
++
++#include <string>
++
++#include <process/future.hpp>
++#include <process/gtest.hpp>
++#include <process/io.hpp>
++
++#include <stout/gtest.hpp>
++#include <stout/os.hpp>
++
++#include "encoder.hpp"
++
++using namespace process;
++
++using std::string;
++
++
++TEST(IO, Poll)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ int pipes[2];
++ ASSERT_NE(-1, pipe(pipes));
++
++ Future<short> future = io::poll(pipes[0], io::READ);
++
++ EXPECT_FALSE(future.isReady());
++
++ ASSERT_EQ(3, write(pipes[1], "hi", 3));
++
++ AWAIT_EXPECT_EQ(io::READ, future);
++
++ close(pipes[0]);
++ close(pipes[1]);
++}
++
++
++TEST(IO, Read)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ int pipes[2];
++ char data[3];
++
++ // Create a blocking pipe.
++ ASSERT_NE(-1, ::pipe(pipes));
++
++ // Test on a blocking file descriptor.
++ AWAIT_EXPECT_FAILED(io::read(pipes[0], data, 3));
++
++ close(pipes[0]);
++ close(pipes[1]);
++
++ // Test on a closed file descriptor.
++ AWAIT_EXPECT_FAILED(io::read(pipes[0], data, 3));
++
++ // Create a nonblocking pipe.
++ ASSERT_NE(-1, ::pipe(pipes));
++ ASSERT_SOME(os::nonblock(pipes[0]));
++ ASSERT_SOME(os::nonblock(pipes[1]));
++
++ // Test reading nothing.
++ AWAIT_EXPECT_FAILED(io::read(pipes[0], data, 0));
++
++ // Test successful read.
++ Future<size_t> future = io::read(pipes[0], data, 3);
++ ASSERT_FALSE(future.isReady());
++
++ ASSERT_EQ(2, write(pipes[1], "hi", 2));
++
++ AWAIT_ASSERT_EQ(2u, future);
++ EXPECT_EQ('h', data[0]);
++ EXPECT_EQ('i', data[1]);
++
++ // Test cancellation.
++ future = io::read(pipes[0], data, 1);
++ ASSERT_FALSE(future.isReady());
++
++ future.discard();
++
++ ASSERT_EQ(3, write(pipes[1], "omg", 3));
++
++ AWAIT_ASSERT_EQ(3u, io::read(pipes[0], data, 3));
++ EXPECT_EQ('o', data[0]);
++ EXPECT_EQ('m', data[1]);
++ EXPECT_EQ('g', data[2]);
++
++ // Test read EOF.
++ future = io::read(pipes[0], data, 3);
++ ASSERT_FALSE(future.isReady());
++
++ close(pipes[1]);
++
++ AWAIT_ASSERT_EQ(0u, future);
++
++ close(pipes[0]);
++}
++
++
++TEST(IO, BufferedRead)
++{
++ // 128 Bytes.
++ string data =
++ "This data is much larger than BUFFERED_READ_SIZE, which means it will "
++ "trigger multiple buffered async reads as a result.........";
++ ASSERT_EQ(128u, data.size());
++
++ // Keep doubling the data size until we're guaranteed to trigger at least
++ // 3 buffered async reads.
++ while (data.length() < 3 * io::BUFFERED_READ_SIZE) {
++ data.append(data);
++ }
++
++ // First read from a file.
++ ASSERT_SOME(os::write("file", data));
++
++ Try<int> fd = os::open("file", O_RDONLY);
++ ASSERT_SOME(fd);
++
++ // Read from blocking fd.
++ AWAIT_EXPECT_FAILED(io::read(fd.get()));
++
++ // Read from non-blocking fd.
++ ASSERT_TRUE(os::nonblock(fd.get()).isSome());
++ AWAIT_EXPECT_EQ(data, io::read(fd.get()));
++
++ os::close(fd.get());
++
++ // Now read from pipes.
++ int pipes[2];
++
++ // Create a blocking pipe.
++ ASSERT_NE(-1, ::pipe(pipes));
++
++ // Test on a blocking pipe.
++ AWAIT_EXPECT_FAILED(io::read(pipes[0]));
++
++ close(pipes[0]);
++ close(pipes[1]);
++
++ // Test on a closed pipe.
++ AWAIT_EXPECT_FAILED(io::read(pipes[0]));
++
++ // Create a nonblocking pipe for reading.
++ ASSERT_NE(-1, ::pipe(pipes));
++ ASSERT_SOME(os::nonblock(pipes[0]));
++
++ // Test a successful read from the pipe.
++ Future<string> future = io::read(pipes[0]);
++
++ // At first, the future will not be ready until we write to and
++ // close the pipe.
++ ASSERT_FALSE(future.isReady());
++
++ ASSERT_SOME(os::write(pipes[1], data));
++ close(pipes[1]);
++
++ AWAIT_EXPECT_EQ(data, future);
++
++ close(pipes[0]);
++
++ ASSERT_SOME(os::rm("file"));
++}
+diff --git a/src/libprocess/tests/main.cpp b/src/libprocess/tests/main.cpp
+new file mode 100644
+index 0000000..6c672b4
+--- /dev/null
++++ b/src/libprocess/tests/main.cpp
+@@ -0,0 +1,25 @@
++#include <gtest/gtest.h>
++
++#include <gmock/gmock.h>
++
++#include <process/gmock.hpp>
++#include <process/gtest.hpp>
++#include <process/process.hpp>
++
++int main(int argc, char** argv)
++{
++ // Initialize Google Mock/Test.
++ testing::InitGoogleMock(&argc, argv);
++
++ // Initialize libprocess.
++ process::initialize();
++
++ // Add the libprocess test event listeners.
++ ::testing::TestEventListeners& listeners =
++ ::testing::UnitTest::GetInstance()->listeners();
++
++ listeners.Append(process::ClockTestEventListener::instance());
++ listeners.Append(process::FilterTestEventListener::instance());
++
++ return RUN_ALL_TESTS();
++}
+diff --git a/src/libprocess/tests/owned_tests.cpp b/src/libprocess/tests/owned_tests.cpp
+new file mode 100644
+index 0000000..234469b
+--- /dev/null
++++ b/src/libprocess/tests/owned_tests.cpp
+@@ -0,0 +1,80 @@
++#include <gmock/gmock.h>
++
++#include <process/gtest.hpp>
++#include <process/owned.hpp>
++#include <process/shared.hpp>
++
++using namespace process;
++
++class Foo
++{
++public:
++ int get() const { return value; }
++ void set(int _value) { value = _value; }
++
++private:
++ int value;
++};
++
++
++TEST(Owned, Access)
++{
++ Foo* foo = new Foo();
++ foo->set(42);
++
++ Owned<Foo> owned(foo);
++
++ EXPECT_EQ(42, owned->get());
++ EXPECT_EQ(42, (*owned).get());
++ EXPECT_EQ(42, owned.get()->get());
++
++ owned->set(10);
++
++ EXPECT_EQ(10, owned->get());
++ EXPECT_EQ(10, (*owned).get());
++ EXPECT_EQ(10, owned.get()->get());
++}
++
++
++TEST(Owned, Null)
++{
++ Owned<Foo> owned;
++ Owned<Foo> owned2(NULL);
++
++ EXPECT_TRUE(owned.get() == NULL);
++ EXPECT_TRUE(owned2.get() == NULL);
++}
++
++
++TEST(Owned, Share)
++{
++ Foo* foo = new Foo();
++ foo->set(42);
++
++ Owned<Foo> owned(foo);
++
++ EXPECT_EQ(42, owned->get());
++ EXPECT_EQ(42, (*owned).get());
++ EXPECT_EQ(42, owned.get()->get());
++
++ Shared<Foo> shared = owned.share();
++
++ EXPECT_TRUE(owned.get() == NULL);
++ EXPECT_TRUE(shared.unique());
++
++ EXPECT_EQ(42, shared->get());
++ EXPECT_EQ(42, (*shared).get());
++ EXPECT_EQ(42, shared.get()->get());
++
++ {
++ Shared<Foo> shared2(shared);
++
++ EXPECT_EQ(42, shared2->get());
++ EXPECT_EQ(42, (*shared2).get());
++ EXPECT_EQ(42, shared2.get()->get());
++ EXPECT_FALSE(shared.unique());
++ EXPECT_FALSE(shared2.unique());
++ }
++
++ EXPECT_TRUE(shared.unique());
++}
+diff --git a/src/libprocess/tests/process_tests.cpp b/src/libprocess/tests/process_tests.cpp
+new file mode 100644
+index 0000000..b0fb5c2
+--- /dev/null
++++ b/src/libprocess/tests/process_tests.cpp
+@@ -0,0 +1,1259 @@
++#include <arpa/inet.h>
++
++#include <gmock/gmock.h>
++
++#include <netinet/in.h>
++#include <netinet/tcp.h>
++
++#include <string>
++#include <sstream>
++
++#include <process/async.hpp>
++#include <process/collect.hpp>
++#include <process/clock.hpp>
++#include <process/defer.hpp>
++#include <process/delay.hpp>
++#include <process/dispatch.hpp>
++#include <process/executor.hpp>
++#include <process/filter.hpp>
++#include <process/future.hpp>
++#include <process/gc.hpp>
++#include <process/gmock.hpp>
++#include <process/gtest.hpp>
++#include <process/limiter.hpp>
++#include <process/process.hpp>
++#include <process/run.hpp>
++#include <process/time.hpp>
++
++#include <stout/duration.hpp>
++#include <stout/gtest.hpp>
++#include <stout/nothing.hpp>
++#include <stout/os.hpp>
++#include <stout/stringify.hpp>
++#include <stout/stopwatch.hpp>
++
++#include "encoder.hpp"
++
++using namespace process;
++
++using std::string;
++
++using testing::_;
++using testing::Assign;
++using testing::DoAll;
++using testing::Return;
++using testing::ReturnArg;
++
++// TODO(bmahler): Move tests into their own files as appropriate.
++
++TEST(Process, event)
++{
++ Event* event = new TerminateEvent(UPID());
++ EXPECT_FALSE(event->is<MessageEvent>());
++ EXPECT_FALSE(event->is<ExitedEvent>());
++ EXPECT_TRUE(event->is<TerminateEvent>());
++ delete event;
++}
++
++
++TEST(Process, future)
++{
++ Promise<bool> promise;
++ promise.set(true);
++ ASSERT_TRUE(promise.future().isReady());
++ EXPECT_TRUE(promise.future().get());
++}
++
++
++TEST(Process, associate)
++{
++ Promise<bool> promise1;
++ Future<bool> future1(true);
++ promise1.associate(future1);
++ ASSERT_TRUE(promise1.future().isReady());
++ EXPECT_TRUE(promise1.future().get());
++
++ Promise<bool> promise2;
++ Future<bool> future2;
++ promise2.associate(future2);
++ future2.discard();
++ ASSERT_TRUE(promise2.future().isDiscarded());
++
++ Promise<bool> promise3;
++ Promise<bool> promise4;
++ promise3.associate(promise4.future());
++ promise4.fail("associate");
++ ASSERT_TRUE(promise3.future().isFailed());
++ EXPECT_EQ("associate", promise3.future().failure());
++
++ // Test that 'discard' is associated in both directions.
++ Promise<bool> promise5;
++ Future<bool> future3;
++ promise5.associate(future3);
++ EXPECT_FALSE(future3.isDiscarded());
++ promise5.future().discard();
++ EXPECT_TRUE(future3.isDiscarded());
++}
++
++
++void onAny(const Future<bool>& future, bool* b)
++{
++ ASSERT_TRUE(future.isReady());
++ *b = future.get();
++}
++
++
++TEST(Process, onAny)
++{
++ bool b = false;
++ Future<bool>(true)
++ .onAny(std::tr1::bind(&onAny, std::tr1::placeholders::_1, &b));
++ EXPECT_TRUE(b);
++}
++
++
++Future<string> itoa1(int* const& i)
++{
++ std::ostringstream out;
++ out << *i;
++ return out.str();
++}
++
++
++string itoa2(int* const& i)
++{
++ std::ostringstream out;
++ out << *i;
++ return out.str();
++}
++
++
++TEST(Process, then)
++{
++ Promise<int*> promise;
++
++ int i = 42;
++
++ promise.set(&i);
++
++ Future<string> future = promise.future()
++ .then(std::tr1::bind(&itoa1, std::tr1::placeholders::_1));
++
++ ASSERT_TRUE(future.isReady());
++ EXPECT_EQ("42", future.get());
++
++ future = promise.future()
++ .then(std::tr1::bind(&itoa2, std::tr1::placeholders::_1));
++
++ ASSERT_TRUE(future.isReady());
++ EXPECT_EQ("42", future.get());
++}
++
++
++Future<bool> readyFuture()
++{
++ return true;
++}
++
++
++Future<bool> failedFuture()
++{
++ return Failure("The value is not positive (or zero)");
++}
++
++
++Future<bool> pendingFuture(Future<bool>* future)
++{
++ return *future; // Keep it pending.
++}
++
++
++Future<string> second(const bool& b)
++{
++ return b ? string("true") : string("false");
++}
++
++
++Future<string> third(const string& s)
++{
++ return s;
++}
++
++
++TEST(Process, chain)
++{
++ Promise<int*> promise;
++
++ Future<string> s = readyFuture()
++ .then(std::tr1::bind(&second, std::tr1::placeholders::_1))
++ .then(std::tr1::bind(&third, std::tr1::placeholders::_1));
++
++ s.await();
++
++ ASSERT_TRUE(s.isReady());
++ EXPECT_EQ("true", s.get());
++
++ s = failedFuture()
++ .then(std::tr1::bind(&second, std::tr1::placeholders::_1))
++ .then(std::tr1::bind(&third, std::tr1::placeholders::_1));
++
++ s.await();
++
++ ASSERT_TRUE(s.isFailed());
++
++ Future<bool> future;
++
++ s = pendingFuture(&future)
++ .then(std::tr1::bind(&second, std::tr1::placeholders::_1))
++ .then(std::tr1::bind(&third, std::tr1::placeholders::_1));
++
++ ASSERT_TRUE(s.isPending());
++ ASSERT_TRUE(future.isPending());
++
++ s.discard();
++
++ future.await();
++
++ ASSERT_TRUE(future.isDiscarded());
++}
++
++
++class SpawnProcess : public Process<SpawnProcess>
++{
++public:
++ MOCK_METHOD0(initialize, void(void));
++ MOCK_METHOD0(finalize, void(void));
++};
++
++
++TEST(Process, spawn)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ SpawnProcess process;
++
++ EXPECT_CALL(process, initialize())
++ .Times(1);
++
++ EXPECT_CALL(process, finalize())
++ .Times(1);
++
++ PID<SpawnProcess> pid = spawn(process);
++
++ ASSERT_FALSE(!pid);
++
++ ASSERT_FALSE(wait(pid, Seconds(0)));
++
++ terminate(pid);
++ wait(pid);
++}
++
++
++class DispatchProcess : public Process<DispatchProcess>
++{
++public:
++ MOCK_METHOD0(func0, void(void));
++ MOCK_METHOD1(func1, bool(bool));
++ MOCK_METHOD1(func2, Future<bool>(bool));
++ MOCK_METHOD1(func3, int(int));
++ MOCK_METHOD2(func4, Future<bool>(bool, int));
++};
++
++
++TEST(Process, dispatch)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ DispatchProcess process;
++
++ EXPECT_CALL(process, func0())
++ .Times(1);
++
++ EXPECT_CALL(process, func1(_))
++ .WillOnce(ReturnArg<0>());
++
++ EXPECT_CALL(process, func2(_))
++ .WillOnce(ReturnArg<0>());
++
++ PID<DispatchProcess> pid = spawn(&process);
++
++ ASSERT_FALSE(!pid);
++
++ dispatch(pid, &DispatchProcess::func0);
++
++ Future<bool> future;
++
++ future = dispatch(pid, &DispatchProcess::func1, true);
++
++ EXPECT_TRUE(future.get());
++
++ future = dispatch(pid, &DispatchProcess::func2, true);
++
++ EXPECT_TRUE(future.get());
++
++ terminate(pid);
++ wait(pid);
++}
++
++
++TEST(Process, defer1)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ DispatchProcess process;
++
++ EXPECT_CALL(process, func0())
++ .Times(1);
++
++ EXPECT_CALL(process, func1(_))
++ .WillOnce(ReturnArg<0>());
++
++ EXPECT_CALL(process, func2(_))
++ .WillOnce(ReturnArg<0>());
++
++ EXPECT_CALL(process, func4(_, _))
++ .WillRepeatedly(ReturnArg<0>());
++
++ PID<DispatchProcess> pid = spawn(&process);
++
++ ASSERT_FALSE(!pid);
++
++ {
++ Deferred<void(void)> func0 =
++ defer(pid, &DispatchProcess::func0);
++ func0();
++ }
++
++ Future<bool> future;
++
++ {
++ Deferred<Future<bool>(void)> func1 =
++ defer(pid, &DispatchProcess::func1, true);
++ future = func1();
++ EXPECT_TRUE(future.get());
++ }
++
++ {
++ Deferred<Future<bool>(void)> func2 =
++ defer(pid, &DispatchProcess::func2, true);
++ future = func2();
++ EXPECT_TRUE(future.get());
++ }
++
++ {
++ Deferred<Future<bool>(void)> func4 =
++ defer(pid, &DispatchProcess::func4, true, 42);
++ future = func4();
++ EXPECT_TRUE(future.get());
++ }
++
++ {
++ Deferred<Future<bool>(bool)> func4 =
++ defer(pid, &DispatchProcess::func4, std::tr1::placeholders::_1, 42);
++ future = func4(false);
++ EXPECT_FALSE(future.get());
++ }
++
++ {
++ Deferred<Future<bool>(int)> func4 =
++ defer(pid, &DispatchProcess::func4, true, std::tr1::placeholders::_1);
++ future = func4(42);
++ EXPECT_TRUE(future.get());
++ }
++
++ // Only take const &!
++
++ terminate(pid);
++ wait(pid);
++}
++
++
++class DeferProcess : public Process<DeferProcess>
++{
++public:
++ Future<string> func1(const Future<int>& f)
++ {
++ return f.then(defer(self(), &Self::_func1, std::tr1::placeholders::_1));
++ }
++
++ Future<string> func2(const Future<int>& f)
++ {
++ return f.then(defer(self(), &Self::_func2));
++ }
++
++private:
++ Future<string> _func1(int i)
++ {
++ return stringify(i);
++ }
++
++ Future<string> _func2()
++ {
++ return string("42");
++ }
++};
++
++
++TEST(Process, defer2)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ DeferProcess process;
++
++ PID<DeferProcess> pid = spawn(process);
++
++ Future<string> f = dispatch(pid, &DeferProcess::func1, 41);
++
++ f.await();
++
++ ASSERT_TRUE(f.isReady());
++ EXPECT_EQ("41", f.get());
++
++ f = dispatch(pid, &DeferProcess::func2, 41);
++
++ f.await();
++
++ ASSERT_TRUE(f.isReady());
++ EXPECT_EQ("42", f.get());
++
++ terminate(pid);
++ wait(pid);
++}
++
++
++template <typename T>
++void set(T* t1, const T& t2)
++{
++ *t1 = t2;
++}
++
++
++TEST(Process, defer3)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ volatile bool bool1 = false;
++ volatile bool bool2 = false;
++
++ Deferred<void(bool)> set1 =
++ defer(std::tr1::function<void(bool)>(
++ std::tr1::bind(&set<volatile bool>,
++ &bool1,
++ std::tr1::placeholders::_1)));
++
++ set1(true);
++
++ Deferred<void(bool)> set2 =
++ defer(std::tr1::function<void(bool)>(
++ std::tr1::bind(&set<volatile bool>,
++ &bool2,
++ std::tr1::placeholders::_1)));
++
++ set2(true);
++
++ while (!bool1);
++ while (!bool2);
++}
++
++
++class HandlersProcess : public Process<HandlersProcess>
++{
++public:
++ HandlersProcess()
++ {
++ install("func", &HandlersProcess::func);
++ }
++
++ MOCK_METHOD2(func, void(const UPID&, const string&));
++};
++
++
++TEST(Process, handlers)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ HandlersProcess process;
++
++ EXPECT_CALL(process, func(_, _))
++ .Times(1);
++
++ PID<HandlersProcess> pid = spawn(&process);
++
++ ASSERT_FALSE(!pid);
++
++ post(pid, "func");
++
++ terminate(pid, false);
++ wait(pid);
++}
++
++
++// Tests EXPECT_MESSAGE and EXPECT_DISPATCH and in particular that an
++// event can get dropped before being processed.
++TEST(Process, expect)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ HandlersProcess process;
++
++ EXPECT_CALL(process, func(_, _))
++ .Times(0);
++
++ PID<HandlersProcess> pid = spawn(&process);
++
++ ASSERT_FALSE(!pid);
++
++ Future<Message> message = DROP_MESSAGE("func", _, _);
++
++ post(pid, "func");
++
++ AWAIT_EXPECT_READY(message);
++
++ Future<Nothing> func = DROP_DISPATCH(pid, &HandlersProcess::func);
++
++ dispatch(pid, &HandlersProcess::func, pid, "");
++
++ AWAIT_EXPECT_READY(func);
++
++ terminate(pid, false);
++ wait(pid);
++}
++
++
++// Tests the FutureArg<N> action.
++TEST(Process, action)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ HandlersProcess process;
++
++ PID<HandlersProcess> pid = spawn(&process);
++
++ ASSERT_FALSE(!pid);
++
++ Future<string> future1;
++ Future<Nothing> future2;
++ EXPECT_CALL(process, func(_, _))
++ .WillOnce(FutureArg<1>(&future1))
++ .WillOnce(FutureSatisfy(&future2));
++
++ dispatch(pid, &HandlersProcess::func, pid, "hello world");
++
++ AWAIT_EXPECT_EQ("hello world", future1);
++
++ EXPECT_TRUE(future2.isPending());
++
++ dispatch(pid, &HandlersProcess::func, pid, "hello world");
++
++ AWAIT_EXPECT_READY(future2);
++
++ terminate(pid, false);
++ wait(pid);
++}
++
++
++class BaseProcess : public Process<BaseProcess>
++{
++public:
++ virtual void func() = 0;
++ MOCK_METHOD0(foo, void());
++};
++
++
++class DerivedProcess : public BaseProcess
++{
++public:
++ DerivedProcess() {}
++ MOCK_METHOD0(func, void());
++};
++
++
++TEST(Process, inheritance)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ DerivedProcess process;
++
++ EXPECT_CALL(process, func())
++ .Times(2);
++
++ EXPECT_CALL(process, foo())
++ .Times(1);
++
++ PID<DerivedProcess> pid1 = spawn(&process);
++
++ ASSERT_FALSE(!pid1);
++
++ dispatch(pid1, &DerivedProcess::func);
++
++ PID<BaseProcess> pid2(process);
++ PID<BaseProcess> pid3 = pid1;
++
++ ASSERT_EQ(pid2, pid3);
++
++ dispatch(pid3, &BaseProcess::func);
++ dispatch(pid3, &BaseProcess::foo);
++
++ terminate(pid1, false);
++ wait(pid1);
++}
++
++
++TEST(Process, thunk)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ struct Thunk
++ {
++ static int run(int i)
++ {
++ return i;
++ }
++
++ static int run(int i, int j)
++ {
++ return run(i + j);
++ }
++ };
++
++ int result = run(&Thunk::run, 21, 21).get();
++
++ EXPECT_EQ(42, result);
++}
++
++
++class DelegatorProcess : public Process<DelegatorProcess>
++{
++public:
++ DelegatorProcess(const UPID& delegatee)
++ {
++ delegate("func", delegatee);
++ }
++};
++
++
++class DelegateeProcess : public Process<DelegateeProcess>
++{
++public:
++ DelegateeProcess()
++ {
++ install("func", &DelegateeProcess::func);
++ }
++
++ MOCK_METHOD2(func, void(const UPID&, const string&));
++};
++
++
++TEST(Process, delegate)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ DelegateeProcess delegatee;
++ DelegatorProcess delegator(delegatee.self());
++
++ EXPECT_CALL(delegatee, func(_, _))
++ .Times(1);
++
++ spawn(&delegator);
++ spawn(&delegatee);
++
++ post(delegator.self(), "func");
++
++ terminate(delegator, false);
++ wait(delegator);
++
++ terminate(delegatee, false);
++ wait(delegatee);
++}
++
++
++class TimeoutProcess : public Process<TimeoutProcess>
++{
++public:
++ MOCK_METHOD0(timeout, void());
++};
++
++
++TEST(Process, delay)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ Clock::pause();
++
++ volatile bool timeoutCalled = false;
++
++ TimeoutProcess process;
++
++ EXPECT_CALL(process, timeout())
++ .WillOnce(Assign(&timeoutCalled, true));
++
++ spawn(process);
++
++ delay(Seconds(5), process.self(), &TimeoutProcess::timeout);
++
++ Clock::advance(Seconds(5));
++
++ while (!timeoutCalled);
++
++ terminate(process);
++ wait(process);
++
++ Clock::resume();
++}
++
++
++class OrderProcess : public Process<OrderProcess>
++{
++public:
++ void order(const PID<TimeoutProcess>& pid)
++ {
++ // TODO(benh): Add a test which uses 'send' instead of dispatch.
++ dispatch(pid, &TimeoutProcess::timeout);
++ }
++};
++
++
++TEST(Process, order)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ Clock::pause();
++
++ TimeoutProcess process1;
++
++ volatile bool timeoutCalled = false;
++
++ EXPECT_CALL(process1, timeout())
++ .WillOnce(Assign(&timeoutCalled, true));
++
++ spawn(process1);
++
++ Time now = Clock::now(&process1);
++
++ Seconds seconds(1);
++
++ Clock::advance(Seconds(1));
++
++ EXPECT_EQ(now, Clock::now(&process1));
++
++ OrderProcess process2;
++ spawn(process2);
++
++ dispatch(process2, &OrderProcess::order, process1.self());
++
++ while (!timeoutCalled);
++
++ EXPECT_EQ(now + seconds, Clock::now(&process1));
++
++ terminate(process1);
++ wait(process1);
++
++ terminate(process2);
++ wait(process2);
++
++ Clock::resume();
++}
++
++
++class DonateProcess : public Process<DonateProcess>
++{
++public:
++ void donate()
++ {
++ DonateProcess process;
++ spawn(process);
++ terminate(process);
++ wait(process);
++ }
++};
++
++
++TEST(Process, donate)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ DonateProcess process;
++ spawn(process);
++
++ dispatch(process, &DonateProcess::donate);
++
++ terminate(process, false);
++ wait(process);
++}
++
++
++class ExitedProcess : public Process<ExitedProcess>
++{
++public:
++ ExitedProcess(const UPID& pid) { link(pid); }
++
++ MOCK_METHOD1(exited, void(const UPID&));
++};
++
++
++TEST(Process, exited)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ UPID pid = spawn(new ProcessBase(), true);
++
++ ExitedProcess process(pid);
++
++ volatile bool exitedCalled = false;
++
++ EXPECT_CALL(process, exited(pid))
++ .WillOnce(Assign(&exitedCalled, true));
++
++ spawn(process);
++
++ terminate(pid);
++
++ while (!exitedCalled);
++
++ terminate(process);
++ wait(process);
++}
++
++
++TEST(Process, select)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ Promise<int> promise1;
++ Promise<int> promise2;
++ Promise<int> promise3;
++ Promise<int> promise4;
++
++ std::set<Future<int> > futures;
++ futures.insert(promise1.future());
++ futures.insert(promise2.future());
++ futures.insert(promise3.future());
++ futures.insert(promise4.future());
++
++ promise1.set(42);
++
++ Future<Future<int> > future = select(futures);
++
++ EXPECT_TRUE(future.await());
++ EXPECT_TRUE(future.isReady());
++ EXPECT_TRUE(future.get().isReady());
++ EXPECT_EQ(42, future.get().get());
++}
++
++
++TEST(Process, collect)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ // First ensure an empty list functions correctly.
++ std::list<Future<int> > empty;
++ Future<std::list<int> > future = collect(empty);
++ AWAIT_ASSERT_READY(future);
++ EXPECT_TRUE(future.get().empty());
++
++ Promise<int> promise1;
++ Promise<int> promise2;
++ Promise<int> promise3;
++ Promise<int> promise4;
++
++ std::list<Future<int> > futures;
++ futures.push_back(promise1.future());
++ futures.push_back(promise2.future());
++ futures.push_back(promise3.future());
++ futures.push_back(promise4.future());
++
++ // Set them out-of-order.
++ promise4.set(4);
++ promise2.set(2);
++ promise1.set(1);
++ promise3.set(3);
++
++ future = collect(futures);
++
++ AWAIT_ASSERT_READY(future);
++
++ std::list<int> values;
++ values.push_back(1);
++ values.push_back(2);
++ values.push_back(3);
++ values.push_back(4);
++
++ // We expect them to be returned in the same order as the
++ // future list that was passed in.
++ EXPECT_EQ(values, future.get());
++}
++
++
++TEST(Process, await)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ // First ensure an empty list functions correctly.
++ std::list<Future<int> > empty;
++ Future<std::list<Future<int> > > future = await(empty);
++ AWAIT_ASSERT_READY(future);
++ EXPECT_TRUE(future.get().empty());
++
++ Promise<int> promise1;
++ Promise<int> promise2;
++ Promise<int> promise3;
++ Promise<int> promise4;
++
++ std::list<Future<int> > futures;
++ futures.push_back(promise1.future());
++ futures.push_back(promise2.future());
++ futures.push_back(promise3.future());
++ futures.push_back(promise4.future());
++
++ // Set them out-of-order.
++ promise4.set(4);
++ promise2.set(2);
++ promise1.set(1);
++ promise3.set(3);
++
++ future = await(futures);
++
++ AWAIT_ASSERT_READY(future);
++
++ EXPECT_EQ(futures.size(), future.get().size());
++
++ // We expect them to be returned in the same order as the
++ // future list that was passed in.
++ int i = 1;
++ foreach (const Future<int>& result, future.get()) {
++ ASSERT_TRUE(result.isReady());
++ ASSERT_EQ(i++, result.get());
++ }
++}
++
++
++class SettleProcess : public Process<SettleProcess>
++{
++public:
++ SettleProcess() : calledDispatch(false) {}
++
++ virtual void initialize()
++ {
++ os::sleep(Milliseconds(10));
++ delay(Seconds(0), self(), &SettleProcess::afterDelay);
++ }
++
++ void afterDelay()
++ {
++ dispatch(self(), &SettleProcess::afterDispatch);
++ os::sleep(Milliseconds(10));
++ TimeoutProcess timeoutProcess;
++ spawn(timeoutProcess);
++ terminate(timeoutProcess);
++ wait(timeoutProcess);
++ }
++
++ void afterDispatch()
++ {
++ os::sleep(Milliseconds(10));
++ calledDispatch = true;
++ }
++
++ volatile bool calledDispatch;
++};
++
++
++TEST(Process, settle)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ Clock::pause();
++ SettleProcess process;
++ spawn(process);
++ Clock::settle();
++ ASSERT_TRUE(process.calledDispatch);
++ terminate(process);
++ wait(process);
++ Clock::resume();
++}
++
++
++TEST(Process, pid)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ TimeoutProcess process;
++
++ PID<TimeoutProcess> pid = process;
++}
++
++
++class Listener1 : public Process<Listener1>
++{
++public:
++ virtual void event1() = 0;
++};
++
++
++class Listener2 : public Process<Listener2>
++{
++public:
++ virtual void event2() = 0;
++};
++
++
++class MultipleListenerProcess
++ : public Process<MultipleListenerProcess>,
++ public Listener1,
++ public Listener2
++{
++public:
++ MOCK_METHOD0(event1, void());
++ MOCK_METHOD0(event2, void());
++};
++
++
++TEST(Process, listener)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ MultipleListenerProcess process;
++
++ EXPECT_CALL(process, event1())
++ .Times(1);
++
++ EXPECT_CALL(process, event2())
++ .Times(1);
++
++ spawn(process);
++
++ dispatch(PID<Listener1>(process), &Listener1::event1);
++ dispatch(PID<Listener2>(process), &Listener2::event2);
++
++ terminate(process, false);
++ wait(process);
++}
++
++
++class EventReceiver
++{
++public:
++ MOCK_METHOD1(event1, void(int));
++ MOCK_METHOD1(event2, void(const string&));
++};
++
++
++TEST(Process, executor)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ volatile bool event1Called = false;
++ volatile bool event2Called = false;
++
++ EventReceiver receiver;
++
++ EXPECT_CALL(receiver, event1(42))
++ .WillOnce(Assign(&event1Called, true));
++
++ EXPECT_CALL(receiver, event2("event2"))
++ .WillOnce(Assign(&event2Called, true));
++
++ Executor executor;
++
++ Deferred<void(int)> event1 =
++ executor.defer(std::tr1::bind(&EventReceiver::event1,
++ &receiver,
++ std::tr1::placeholders::_1));
++
++ event1(42);
++
++ Deferred<void(const string&)> event2 =
++ executor.defer(std::tr1::bind(&EventReceiver::event2,
++ &receiver,
++ std::tr1::placeholders::_1));
++
++ event2("event2");
++
++ while (!event1Called);
++ while (!event2Called);
++}
++
++
++class RemoteProcess : public Process<RemoteProcess>
++{
++public:
++ RemoteProcess()
++ {
++ install("handler", &RemoteProcess::handler);
++ }
++
++ MOCK_METHOD2(handler, void(const UPID&, const string&));
++};
++
++
++TEST(Process, remote)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ RemoteProcess process;
++
++ volatile bool handlerCalled = false;
++
++ EXPECT_CALL(process, handler(_, _))
++ .WillOnce(Assign(&handlerCalled, true));
++
++ spawn(process);
++
++ int s = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
++
++ ASSERT_LE(0, s);
++
++ sockaddr_in addr;
++ memset(&addr, 0, sizeof(addr));
++ addr.sin_family = PF_INET;
++ addr.sin_port = htons(process.self().port);
++ addr.sin_addr.s_addr = process.self().ip;
++
++ ASSERT_EQ(0, connect(s, (sockaddr*) &addr, sizeof(addr)));
++
++ Message message;
++ message.name = "handler";
++ message.from = UPID();
++ message.to = process.self();
++
++ const string& data = MessageEncoder::encode(&message);
++
++ ASSERT_EQ(data.size(), write(s, data.data(), data.size()));
++
++ ASSERT_EQ(0, close(s));
++
++ while (!handlerCalled);
++
++ terminate(process);
++ wait(process);
++}
++
++
++int foo()
++{
++ return 1;
++}
++
++int foo1(int a)
++{
++ return a;
++}
++
++
++int foo2(int a, int b)
++{
++ return a + b;
++}
++
++
++int foo3(int a, int b, int c)
++{
++ return a + b + c;
++}
++
++
++int foo4(int a, int b, int c, int d)
++{
++ return a + b + c + d;
++}
++
++
++void bar(int a)
++{
++ return;
++}
++
++
++TEST(Process, async)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ // Non-void functions with different no.of args.
++ EXPECT_EQ(1, async(&foo).get());
++ EXPECT_EQ(10, async(&foo1, 10).get());
++ EXPECT_EQ(30, async(&foo2, 10, 20).get());
++ EXPECT_EQ(60, async(&foo3, 10, 20, 30).get());
++ EXPECT_EQ(100, async(&foo4, 10, 20, 30, 40).get());
++
++ // Non-void function with a complex arg.
++ int i = 42;
++ EXPECT_EQ("42", async(&itoa2, &i).get());
++
++ // Non-void function that returns a future.
++ EXPECT_EQ("42", async(&itoa1, &i).get().get());
++}
++
++
++TEST(Process, limiter)
++{
++ ASSERT_TRUE(GTEST_IS_THREADSAFE);
++
++ int permits = 2;
++ Duration duration = Milliseconds(5);
++
++ RateLimiter limiter(permits, duration);
++ Milliseconds interval = duration / permits;
++
++ Stopwatch stopwatch;
++ stopwatch.start();
++
++ Future<Nothing> acquire1 = limiter.acquire();
++ Future<Nothing> acquire2 = limiter.acquire();
++ Future<Nothing> acquire3 = limiter.acquire();
++
++ AWAIT_READY(acquire1);
++
++ AWAIT_READY(acquire2);
++ ASSERT_LE(interval, stopwatch.elapsed());
++
++ AWAIT_READY(acquire3);
++ ASSERT_LE(interval * 2, stopwatch.elapsed());
++}
++
++
++class FileServer : public Process<FileServer>
++{
++public:
++ FileServer(const string& _path)
++ : path(_path) {}
++
++ virtual void initialize()
++ {
++ provide("", path);
++ }
++
++ const string path;
++};
++
++
++TEST(Process, provide)
++{
++ const Try<string>& mkdtemp = os::mkdtemp();
++ ASSERT_SOME(mkdtemp);
++
++ const string LOREM_IPSUM =
++ "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
++ "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad "
++ "minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip "
++ "ex ea commodo consequat. Duis aute irure dolor in reprehenderit in "
++ "voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur "
++ "sint occaecat cupidatat non proident, sunt in culpa qui officia "
++ "deserunt mollit anim id est laborum.";
++
++ const string path = path::join(mkdtemp.get(), "lorem.txt");
++ ASSERT_SOME(os::write(path, LOREM_IPSUM));
++
++ FileServer server(path);
++ PID<FileServer> pid = spawn(server);
++
++ Future<http::Response> response = http::get(pid);
++
++ AWAIT_READY(response);
++
++ ASSERT_EQ(LOREM_IPSUM, response.get().body);
++
++ terminate(server);
++ wait(server);
++
++ ASSERT_SOME(os::rmdir(path));
++}
+diff --git a/src/libprocess/tests/shared_tests.cpp b/src/libprocess/tests/shared_tests.cpp
+new file mode 100644
+index 0000000..1df67b4
+--- /dev/null
++++ b/src/libprocess/tests/shared_tests.cpp
+@@ -0,0 +1,109 @@
++#include <gmock/gmock.h>
++
++#include <process/gtest.hpp>
++#include <process/owned.hpp>
++#include <process/shared.hpp>
++
++using namespace process;
++
++class Foo
++{
++public:
++ int get() const { return value; }
++ void set(int _value) { value = _value; }
++
++private:
++ int value;
++};
++
++
++TEST(Shared, ConstAccess)
++{
++ Foo* foo = new Foo();
++ foo->set(10);
++
++ Shared<Foo> shared(foo);
++
++ EXPECT_EQ(10, shared->get());
++
++ // The following won't compile.
++ // shared->set(20);
++}
++
++
++TEST(Shared, Null)
++{
++ Shared<Foo> shared(NULL);
++ Shared<Foo> shared2(shared);
++
++ EXPECT_TRUE(shared.get() == NULL);
++ EXPECT_TRUE(shared2.get() == NULL);
++}
++
++
++TEST(Shared, Reset)
++{
++ Foo* foo = new Foo();
++ foo->set(42);
++
++ Shared<Foo> shared(foo);
++ Shared<Foo> shared2(shared);
++
++ EXPECT_FALSE(shared.unique());
++ EXPECT_FALSE(shared2.unique());
++ EXPECT_EQ(42, shared->get());
++ EXPECT_EQ(42, shared2->get());
++
++ shared.reset();
++
++ EXPECT_FALSE(shared.unique());
++ EXPECT_TRUE(shared.get() == NULL);
++
++ EXPECT_TRUE(shared2.unique());
++ EXPECT_EQ(42, shared2->get());
++}
++
++
++TEST(Shared, Own)
++{
++ Foo* foo = new Foo();
++ foo->set(42);
++
++ Shared<Foo> shared(foo);
++
++ EXPECT_EQ(42, shared->get());
++ EXPECT_EQ(42, (*shared).get());
++ EXPECT_EQ(42, shared.get()->get());
++ EXPECT_TRUE(shared.unique());
++
++ Future<Owned<Foo> > future;
++
++ {
++ Shared<Foo> shared2(shared);
++
++ EXPECT_EQ(42, shared2->get());
++ EXPECT_EQ(42, (*shared2).get());
++ EXPECT_EQ(42, shared2.get()->get());
++ EXPECT_FALSE(shared2.unique());
++ EXPECT_FALSE(shared.unique());
++
++ future = shared2.own();
++
++ // A shared pointer will be reset after it called 'own'.
++ EXPECT_TRUE(shared2.get() == NULL);
++
++ // Do not allow 'own' to be called twice.
++ AWAIT_FAILED(shared.own());
++
++ // Not "owned" yet as 'shared' is still holding the reference.
++ EXPECT_TRUE(future.isPending());
++ }
++
++ shared.reset();
++ AWAIT_READY(future);
++
++ Owned<Foo> owned = future.get();
++ EXPECT_EQ(42, owned->get());
++ EXPECT_EQ(42, (*owned).get());
++ EXPECT_EQ(42, owned.get()->get());
++}
+diff --git a/src/libprocess/tests/statistics_tests.cpp b/src/libprocess/tests/statistics_tests.cpp
+new file mode 100644
+index 0000000..e6c9a1b
+--- /dev/null
++++ b/src/libprocess/tests/statistics_tests.cpp
+@@ -0,0 +1,190 @@
++#include <gmock/gmock.h>
++
++#include <map>
++
++#include <process/clock.hpp>
++#include <process/future.hpp>
++#include <process/gtest.hpp>
++#include <process/statistics.hpp>
++#include <process/time.hpp>
++
++#include <stout/duration.hpp>
++
++using namespace process;
++
++using std::map;
++
++
++TEST(Statistics, set)
++{
++ Statistics statistics(Days(1));
++
++ // Set one using Clock::now() implicitly.
++ statistics.set("test", "statistic", 3.0);
++
++ // Set one using Clock::now() explicitly.
++ Time now = Clock::now();
++ statistics.set("test", "statistic", 4.0, now);
++
++ Future<map<Time, double> > values =
++ statistics.timeseries("test", "statistic");
++
++ AWAIT_ASSERT_READY(values);
++
++ EXPECT_EQ(2, values.get().size());
++
++ EXPECT_GE(Clock::now(), values.get().begin()->first);
++ EXPECT_DOUBLE_EQ(3.0, values.get().begin()->second);
++
++ EXPECT_EQ(1, values.get().count(now));
++ EXPECT_DOUBLE_EQ(4.0, values.get()[now]);
++}
++
++
++TEST(Statistics, truncate)
++{
++ Clock::pause();
++
++ Statistics statistics(Days(1));
++
++ statistics.set("test", "statistic", 3.0);
++
++ Future<map<Time, double> > values =
++ statistics.timeseries("test", "statistic");
++
++ AWAIT_ASSERT_READY(values);
++
++ EXPECT_EQ(1, values.get().size());
++ EXPECT_GE(Clock::now(), values.get().begin()->first);
++ EXPECT_DOUBLE_EQ(3.0, values.get().begin()->second);
++
++ Clock::advance(Days(1) + Seconds(1));
++ Clock::settle();
++
++ statistics.increment("test", "statistic");
++
++ values = statistics.timeseries("test", "statistic");
++
++ AWAIT_ASSERT_READY(values);
++
++ EXPECT_EQ(1, values.get().size());
++ EXPECT_GE(Clock::now(), values.get().begin()->first);
++ EXPECT_DOUBLE_EQ(4.0, values.get().begin()->second);
++
++ Clock::resume();
++}
++
++
++TEST(Statistics, meter) {
++ Statistics statistics(Days(1));
++
++ // Set up a meter, and ensure it captures the expected time rate.
++ Future<Try<Nothing> > meter =
++ statistics.meter(
++ "test",
++ "statistic",
++ Owned<meters::Meter>(new meters::TimeRate("metered")));
++
++ AWAIT_ASSERT_READY(meter);
++
++ ASSERT_TRUE(meter.get().isSome());
++
++ Time now = Clock::now();
++ statistics.set("test", "statistic", 1.0, now);
++ statistics.set("test", "statistic", 2.0, Time(now + Seconds(1)));
++ statistics.set("test", "statistic", 4.0, Time(now + Seconds(2)));
++
++ // Check the raw statistic values.
++ Future<map<Time, double> > values =
++ statistics.timeseries("test", "statistic");
++
++ AWAIT_ASSERT_READY(values);
++
++ EXPECT_EQ(3, values.get().size());
++ EXPECT_EQ(1, values.get().count(now));
++ EXPECT_EQ(1, values.get().count(Time(now + Seconds(1))));
++ EXPECT_EQ(1, values.get().count(Time(now + Seconds(2))));
++
++ EXPECT_EQ(1.0, values.get()[now]);
++ EXPECT_EQ(2.0, values.get()[Time(now + Seconds(1))]);
++ EXPECT_EQ(4.0, values.get()[Time(now + Seconds(2))]);
++
++ // Now check the metered values.
++ values = statistics.timeseries("test", "metered");
++
++ AWAIT_ASSERT_READY(values);
++
++ EXPECT_EQ(2, values.get().size());
++ EXPECT_EQ(1, values.get().count(Time(now + Seconds(1))));
++ EXPECT_EQ(1, values.get().count(Time(now + Seconds(2))));
++
++ EXPECT_EQ(0., values.get()[now]);
++ EXPECT_EQ(1.0, values.get()[Time(now + Seconds(1))]); // 100%.
++ EXPECT_EQ(2.0, values.get()[Time(now + Seconds(2))]); // 200%.
++}
++
++
++TEST(Statistics, archive)
++{
++ Clock::pause();
++
++ Statistics statistics(Seconds(10));
++
++ // Create a meter and a statistic for archival.
++ // Set up a meter, and ensure it captures the expected time rate.
++ Future<Try<Nothing> > meter =
++ statistics.meter(
++ "test",
++ "statistic",
++ Owned<meters::Meter>(new meters::TimeRate("metered")));
++
++ AWAIT_ASSERT_READY(meter);
++
++ ASSERT_TRUE(meter.get().isSome());
++
++ Time now = Clock::now();
++ statistics.set("test", "statistic", 1.0, now);
++ statistics.set("test", "statistic", 2.0, Time(now + Seconds(1)));
++
++ // Archive and ensure the following:
++ // 1. The statistic will no longer be part of the snapshot.
++ // 2. Any meters associated with this statistic will be removed.
++ // 3. However, the time series will be retained until the window expiration.
++ statistics.archive("test", "statistic");
++
++ // TODO(bmahler): Wait for JSON parsing to verify number 1.
++
++ // Ensure the raw time series is present.
++ Future<map<Time, double> > values =
++ statistics.timeseries("test", "statistic");
++ AWAIT_ASSERT_READY(values);
++ EXPECT_FALSE(values.get().empty());
++
++ // Ensure the metered timeseries is present.
++ values = statistics.timeseries("test", "metered");
++ AWAIT_ASSERT_READY(values);
++ EXPECT_FALSE(values.get().empty());
++
++ // Expire the window and ensure the statistics were removed.
++ Clock::advance(STATISTICS_TRUNCATION_INTERVAL);
++ Clock::settle();
++
++ // Ensure the raw statistics are gone.
++ values = statistics.timeseries("test", "statistic");
++ AWAIT_ASSERT_READY(values);
++ EXPECT_TRUE(values.get().empty());
++
++ // Ensure the metered statistics are gone.
++ values = statistics.timeseries("test", "metered");
++ AWAIT_ASSERT_READY(values);
++ EXPECT_TRUE(values.get().empty());
++
++ // Reactivate the statistic, and make sure the meter is still missing.
++ statistics.set("test", "statistic", 1.0, now);
++
++ values = statistics.timeseries("test", "metered");
++ AWAIT_ASSERT_READY(values);
++ EXPECT_TRUE(values.get().empty());
++
++ Clock::resume();
++}
+diff --git a/src/libprocess/tests/time_tests.cpp b/src/libprocess/tests/time_tests.cpp
+new file mode 100644
+index 0000000..a25827e
+--- /dev/null
++++ b/src/libprocess/tests/time_tests.cpp
+@@ -0,0 +1,46 @@
++#include <gtest/gtest.h>
++
++#include <gmock/gmock.h>
++
++#include <process/clock.hpp>
++#include <process/time.hpp>
++
++#include <stout/duration.hpp>
++#include <stout/gtest.hpp>
++#include <stout/os.hpp>
++
++using namespace process;
++
++
++TEST(TimeTest, Arithmetic)
++{
++ Time t = Time::EPOCH + Weeks(1000);
++ t -= Weeks(1);
++ EXPECT_EQ(Time::EPOCH + Weeks(999), t);
++
++ t += Weeks(2);
++ EXPECT_EQ(Time::EPOCH + Weeks(1001), t);
++
++ EXPECT_EQ(t, Time::EPOCH + Weeks(1000) + Weeks(1));
++ EXPECT_EQ(t, Time::EPOCH + Weeks(1002) - Weeks(1));
++
++ EXPECT_EQ(Weeks(1), (Time::EPOCH + Weeks(1000)) - (Time::EPOCH + Weeks(999)));
++}
++
++
++TEST(TimeTest, Now)
++{
++ Time t1 = Clock::now();
++ os::sleep(Microseconds(10));
++ ASSERT_LT(Microseconds(10), Clock::now() - t1);
++}
++
++
++TEST(TimeTest, Output)
++{
++ EXPECT_EQ("1989-03-02 00:00:00+00:00", stringify(Time::EPOCH + Weeks(1000)));
++ EXPECT_EQ("1989-03-02 00:00:00.000000001+00:00",
++ stringify(Time::EPOCH + Weeks(1000) + Nanoseconds(1)));
++ EXPECT_EQ("1989-03-02 00:00:00.000001000+00:00",
++ stringify(Time::EPOCH + Weeks(1000) + Microseconds(1)));
++}
+diff --git a/src/libprocess/timer.cpp b/src/libprocess/timer.cpp
+new file mode 100644
+index 0000000..63c5ac1
+--- /dev/null
++++ b/src/libprocess/timer.cpp
+@@ -0,0 +1,56 @@
++#include <process/timer.hpp>
++
++#include "timeout.hpp"
++
++namespace process {
++
++class TimerProcess : public Process<TimerProcess>
++{
++public:
++ TimerProcess(double _secs,
++ const UPID& _pid,
++ std::tr1::function<void(ProcessBase*)>* _dispatcher)
++ : secs(_secs), pid(_pid), dispatcher(_dispatcher) {}
++
++protected:
++ virtual void operator () ()
++ {
++ if (receive(secs) == TIMEOUT) {
++ internal::dispatch(pid, dispatcher);
++ } else {
++ delete dispatcher;
++ }
++ }
++
++private:
++ const double secs;
++ const UPID pid;
++ std::tr1::function<void(ProcessBase*)>* dispatcher;
++};
++
++
++static void dispatch()
++
++
++Timer::Timer(double secs,
++ const UPID& pid,
++ std::tr1::function<void(ProcessBase*)>* dispatcher)
++{
++ timer = spawn(new TimerProcess(secs, pid, dispatcher), true);
++}
++
++
++Timer::~Timer()
++{
++ // NOTE: Do not terminate the timer! Some users will simply ignore
++ // saving the timer because they never want to cancel, thus
++ // we can not terminate it here!
++}
++
++
++void Timer::cancel()
++{
++ timeouts::cancel(timeout);
++}
++
++} // namespace process {
+diff --git a/src/libprocess/timer.hpp b/src/libprocess/timer.hpp
+new file mode 100644
+index 0000000..443b5a0
+--- /dev/null
++++ b/src/libprocess/timer.hpp
+@@ -0,0 +1,125 @@
++#ifndef TIMER_HPP
++#define TIMER_HPP
++
++#include <ctime>
++#include <iostream>
++#include <iomanip>
++
++class timer
++{
++ friend std::ostream& operator<<(std::ostream& os, timer& t);
++
++private:
++ bool running;
++ clock_t start_clock;
++ time_t start_time;
++ double acc_time;
++
++ double elapsed_time();
++
++public:
++ // 'running' is initially false. A timer needs to be explicitly started
++ // using 'start' or 'restart'
++ timer() : running(false), start_clock(0), start_time(0), acc_time(0) { }
++
++ void start(const char* msg = 0);
++ void restart(const char* msg = 0);
++ void stop(const char* msg = 0);
++ void check(const char* msg = 0);
++
++}; // class timer
++
++//===========================================================================
++// Return the total time that the timer has been in the "running"
++// state since it was first "started" or last "restarted". For
++// "short" time periods (less than an hour), the actual cpu time
++// used is reported instead of the elapsed time.
++
++inline double timer::elapsed_time()
++{
++ time_t acc_sec = time(0) - start_time;
++ if (acc_sec < 3600)
++ return (clock() - start_clock) / (1.0 * CLOCKS_PER_SEC);
++ else
++ return (1.0 * acc_sec);
++
++} // timer::elapsed_time
++
++//===========================================================================
++// Start a timer. If it is already running, let it continue running.
++// Print an optional message.
++
++inline void timer::start(const char* msg)
++{
++ // Print an optional message, something like "Starting timer t";
++ if (msg) std::cout << msg << std::endl;
++
++ // Return immediately if the timer is already running
++ if (running) return;
++
++ // Set timer status to running and set the start time
++ running = true;
++ start_clock = clock();
++ start_time = time(0);
++
++} // timer::start
++
++//===========================================================================
++// Turn the timer off and start it again from 0. Print an optional message.
++
++inline void timer::restart(const char* msg)
++{
++ // Print an optional message, something like "Restarting timer t";
++ if (msg) std::cout << msg << std::endl;
++
++ // Set timer status to running, reset accumulated time, and set start time
++ running = true;
++ acc_time = 0;
++ start_clock = clock();
++ start_time = time(0);
++
++} // timer::restart
++
++//===========================================================================
++// Stop the timer and print an optional message.
++
++inline void timer::stop(const char* msg)
++{
++ // Print an optional message, something like "Stopping timer t";
++ if (msg) std::cout << msg << std::endl;
++
++ // Compute accumulated running time and set timer status to not running
++ if (running) acc_time += elapsed_time();
++ running = false;
++
++} // timer::stop
++
++//===========================================================================
++// Print out an optional message followed by the current timer timing.
++
++inline void timer::check(const char* msg)
++{
++ // Print an optional message, something like "Checking timer t";
++ if (msg) std::cout << msg << " : ";
++
++ std::cout << "Elapsed time [" << std::setiosflags(std::ios::fixed)
++ << std::setprecision(2)
++ << acc_time + (running ? elapsed_time() : 0) << "] seconds\n";
++
++} // timer::check
++
++//===========================================================================
++// Allow timers to be printed to ostreams using the syntax 'os << t'
++// for an ostream 'os' and a timer 't'. For example, "cout << t" will
++// print out the total amount of time 't' has been "running".
++
++inline std::ostream& operator<<(std::ostream& os, timer& t)
++{
++ os << std::setprecision(2) << std::setiosflags(std::ios::fixed)
++ << t.acc_time + (t.running ? t.elapsed_time() : 0);
++ return os;
++}
++
++//===========================================================================
++
++#endif /* TIMER_HPP */
+diff --git a/src/tests/stout/bytes_tests.cpp b/src/tests/stout/bytes_tests.cpp
+new file mode 100644
+index 0000000..18b2474
+--- /dev/null
++++ b/src/tests/stout/bytes_tests.cpp
+@@ -0,0 +1,38 @@
++#include <gtest/gtest.h>
++
++#include <gmock/gmock.h>
++
++#include <stout/bytes.hpp>
++#include <stout/gtest.hpp>
++#include <stout/stringify.hpp>
++#include <stout/try.hpp>
++
++
++TEST(Stout, Bytes)
++{
++ Try<Bytes> _1terabyte = Bytes::parse("1TB");
++
++ EXPECT_SOME_EQ(Terabytes(1), _1terabyte);
++ EXPECT_SOME_EQ(Gigabytes(1024), _1terabyte);
++ EXPECT_SOME_EQ(Megabytes(1024 * 1024), _1terabyte);
++ EXPECT_SOME_EQ(Kilobytes(1024 * 1024 * 1024), _1terabyte);
++ EXPECT_SOME_EQ(Bytes(1024LLU * 1024 * 1024 * 1024), _1terabyte);
++
++ EXPECT_EQ(Bytes(1024), Kilobytes(1));
++ EXPECT_LT(Bytes(1023), Kilobytes(1));
++ EXPECT_GT(Bytes(1025), Kilobytes(1));
++
++ EXPECT_NE(Megabytes(1023), Gigabytes(1));
++
++ EXPECT_EQ("0B", stringify(Bytes()));
++
++ EXPECT_EQ("1KB", stringify(Kilobytes(1)));
++ EXPECT_EQ("1MB", stringify(Megabytes(1)));
++ EXPECT_EQ("1GB", stringify(Gigabytes(1)));
++ EXPECT_EQ("1TB", stringify(Terabytes(1)));
++
++ EXPECT_EQ("1023B", stringify(Bytes(1023)));
++ EXPECT_EQ("1023KB", stringify(Kilobytes(1023)));
++ EXPECT_EQ("1023MB", stringify(Megabytes(1023)));
++ EXPECT_EQ("1023GB", stringify(Gigabytes(1023)));
++}
+diff --git a/src/tests/stout/duration_tests.cpp b/src/tests/stout/duration_tests.cpp
+new file mode 100644
+index 0000000..4269d3c
+--- /dev/null
++++ b/src/tests/stout/duration_tests.cpp
+@@ -0,0 +1,100 @@
++#include <gtest/gtest.h>
++
++#include <gmock/gmock.h>
++
++#include <stout/duration.hpp>
++#include <stout/gtest.hpp>
++#include <stout/stringify.hpp>
++#include <stout/try.hpp>
++
++
++TEST(DurationTest, Comparison)
++{
++ EXPECT_EQ(Duration::zero(), Seconds(0));
++ EXPECT_EQ(Minutes(180), Hours(3));
++ EXPECT_EQ(Seconds(10800), Hours(3));
++ EXPECT_EQ(Milliseconds(10800000), Hours(3));
++
++ EXPECT_EQ(Milliseconds(1), Microseconds(1000));
++ EXPECT_EQ(Milliseconds(1000), Seconds(1));
++
++ EXPECT_GT(Weeks(1), Days(6));
++
++ EXPECT_LT(Hours(23), Days(1));
++
++ EXPECT_LE(Hours(24), Days(1));
++ EXPECT_GE(Hours(24), Days(1));
++
++ EXPECT_NE(Minutes(59), Hours(1));
++
++ // Maintains precision for a 100 year duration.
++ EXPECT_GT(Weeks(5217) + Nanoseconds(1), Weeks(5217));
++ EXPECT_LT(Weeks(5217) - Nanoseconds(1), Weeks(5217));
++}
++
++TEST(DurationTest, ParseAndTry)
++{
++ EXPECT_SOME_EQ(Hours(3), Duration::parse("3hrs"));
++ EXPECT_SOME_EQ(Hours(3) + Minutes(30), Duration::parse("3.5hrs"));
++
++ EXPECT_SOME_EQ(Nanoseconds(3141592653), Duration::create(3.141592653));
++ // Duration can hold only 9.22337e9 seconds.
++ EXPECT_ERROR(Duration::create(10 * 1e9));
++ EXPECT_ERROR(Duration::create(-10 * 1e9));
++}
++
++TEST(DurationTest, Arithmetic)
++{
++ Duration d = Seconds(11);
++ d += Seconds(9);
++ EXPECT_EQ(Seconds(20), d);
++
++ d = Seconds(11);
++ d -= Seconds(21);
++ EXPECT_EQ(Seconds(-10), d);
++
++ d = Seconds(10);
++ d *= 2;
++ EXPECT_EQ(Seconds(20), d);
++
++ d = Seconds(10);
++ d /= 2.5;
++ EXPECT_EQ(Seconds(4), d);
++
++ EXPECT_EQ(Seconds(20), Seconds(11) + Seconds(9));
++ EXPECT_EQ(Seconds(-10), Seconds(11) - Seconds(21));
++ EXPECT_EQ(Duration::create(3.3).get(), Seconds(10) * 0.33);
++ EXPECT_EQ(Duration::create(1.25).get(), Seconds(10) / 8);
++
++ EXPECT_EQ(Duration::create(Days(11).secs() + 9).get(), Days(11) + Seconds(9));
++}
++
++
++TEST(DurationTest, OutputFormat)
++{
++ EXPECT_EQ("1ns", stringify(Nanoseconds(1)));
++ EXPECT_EQ("2ns", stringify(Nanoseconds(2)));
++
++ // Truncated. Seconds in 15 digits of precision, max of double
++ // type's precise digits.
++ EXPECT_EQ("3.141592653secs",
++ stringify(Duration::create(3.14159265358979).get()));
++ EXPECT_EQ("3140ms", stringify(Duration::create(3.14).get()));
++ EXPECT_EQ("10hrs", stringify(Hours(10)));
++ EXPECT_EQ("-10hrs", stringify(Hours(-10)));
++
++ // "10days" reads better than "1.42857142857143weeks" so it is
++ // printed out in the lower unit.
++ EXPECT_EQ("10days", stringify(Days(10)));
++ // We go one-level down and it is still not a whole number so we
++ // print it out using the higher unit.
++ EXPECT_EQ("1.1875days", stringify(Days(1) + Hours(4) + Minutes(30)));
++ // "2weeks" reads better than "14days" so we use the higher unit
++ // here.
++ EXPECT_EQ("2weeks", stringify(Days(14)));
++
++ // Boundary cases.
++ EXPECT_EQ("0ns", stringify(Duration::zero()));
++ EXPECT_EQ("15250.2844524715weeks", stringify(Duration::max()));
++ EXPECT_EQ("-15250.2844524715weeks", stringify(Duration::min()));
++}
+diff --git a/src/tests/stout/error_tests.cpp b/src/tests/stout/error_tests.cpp
+new file mode 100644
+index 0000000..75e365e
+--- /dev/null
++++ b/src/tests/stout/error_tests.cpp
+@@ -0,0 +1,60 @@
++#include <gtest/gtest.h>
++
++#include <gmock/gmock.h>
++
++#include <string>
++
++#include <stout/error.hpp>
++#include <stout/option.hpp>
++#include <stout/result.hpp>
++#include <stout/try.hpp>
++
++using std::string;
++
++
++Error error1()
++{
++ return Error("Failed to ...");
++}
++
++
++Try<string> error2()
++{
++ return Error("Failed to ...");
++}
++
++
++Try<string> error3(const Try<string>& t)
++{
++ return t;
++}
++
++
++Result<string> error4()
++{
++ return Error("Failed to ...");
++}
++
++
++Result<string> error5(const Result<string>& r)
++{
++ return r;
++}
++
++
++TEST(ErrorTest, Test)
++{
++ Try<string> t = error1();
++ EXPECT_TRUE(t.isError());
++ t = error2();
++ EXPECT_TRUE(t.isError());
++ t = error3(error1());
++ EXPECT_TRUE(t.isError());
++
++ Result<string> r = error1();
++ EXPECT_TRUE(r.isError());
++ r = error4();
++ EXPECT_TRUE(r.isError());
++ r = error5(error1());
++ EXPECT_TRUE(r.isError());
++}
+diff --git a/src/tests/stout/flags_tests.cpp b/src/tests/stout/flags_tests.cpp
+new file mode 100644
+index 0000000..9af2da1
+--- /dev/null
++++ b/src/tests/stout/flags_tests.cpp
+@@ -0,0 +1,382 @@
++#include <gmock/gmock.h>
++
++#include <map>
++#include <string>
++
++#include <stout/duration.hpp>
++#include <stout/flags.hpp>
++#include <stout/gtest.hpp>
++#include <stout/none.hpp>
++#include <stout/nothing.hpp>
++#include <stout/option.hpp>
++#include <stout/os.hpp>
++#include <stout/some.hpp>
++
++
++using namespace flags;
++
++class TestFlags : public virtual FlagsBase
++{
++public:
++ TestFlags()
++ {
++ add(&TestFlags::name1,
++ "name1",
++ "Set name1",
++ "ben folds");
++
++ add(&TestFlags::name2,
++ "name2",
++ "Set name2",
++ 42);
++
++ add(&TestFlags::name3,
++ "name3",
++ "Set name3",
++ false);
++
++ add(&TestFlags::name4,
++ "name4",
++ "Set name4");
++
++ add(&TestFlags::name5,
++ "name5",
++ "Set name5");
++ }
++
++ std::string name1;
++ int name2;
++ bool name3;
++ Option<bool> name4;
++ Option<bool> name5;
++};
++
++
++TEST(FlagsTest, Load)
++{
++ TestFlags flags;
++
++ std::map<std::string, Option<std::string> > values;
++
++ values["name1"] = Some("billy joel");
++ values["name2"] = Some("43");
++ values["name3"] = Some("false");
++ values["no-name4"] = None();
++ values["name5"] = None();
++
++ flags.load(values);
++
++ EXPECT_EQ("billy joel", flags.name1);
++ EXPECT_EQ(43, flags.name2);
++ EXPECT_FALSE(flags.name3);
++ ASSERT_SOME(flags.name4);
++ EXPECT_FALSE(flags.name4.get());
++ ASSERT_SOME(flags.name5);
++ EXPECT_TRUE(flags.name5.get());
++}
++
++
++TEST(FlagsTest, Add)
++{
++ Flags<TestFlags> flags;
++
++ Option<std::string> name6;
++
++ flags.add(&name6,
++ "name6",
++ "Also set name6");
++
++ bool name7;
++
++ flags.add(&name7,
++ "name7",
++ "Also set name7",
++ true);
++
++ Option<std::string> name8;
++
++ flags.add(&name8,
++ "name8",
++ "Also set name8");
++
++ std::map<std::string, Option<std::string> > values;
++
++ values["name6"] = Some("ben folds");
++ values["no-name7"] = None();
++
++ flags.load(values);
++
++ ASSERT_SOME(name6);
++ EXPECT_EQ("ben folds", name6.get());
++
++ EXPECT_FALSE(name7);
++
++ ASSERT_TRUE(name8.isNone());
++}
++
++
++TEST(FlagsTest, Flags)
++{
++ TestFlags flags;
++
++ std::map<std::string, Option<std::string> > values;
++
++ values["name1"] = Some("billy joel");
++ values["name2"] = Some("43");
++ values["name3"] = Some("false");
++ values["no-name4"] = None();
++ values["name5"] = None();
++
++ flags.load(values);
++
++ EXPECT_EQ("billy joel", flags.name1);
++ EXPECT_EQ(43, flags.name2);
++ EXPECT_FALSE(flags.name3);
++ ASSERT_SOME(flags.name4);
++ EXPECT_FALSE(flags.name4.get());
++ ASSERT_SOME(flags.name5);
++ EXPECT_TRUE(flags.name5.get());
++}
++
++
++TEST(FlagsTest, LoadFromEnvironment)
++{
++ TestFlags flags;
++
++ os::setenv("FLAGSTEST_name1", "billy joel");
++ os::setenv("FLAGSTEST_name2", "43");
++ os::setenv("FLAGSTEST_no-name3", "");
++ os::setenv("FLAGSTEST_no-name4", "");
++ os::setenv("FLAGSTEST_name5", "");
++
++ Try<Nothing> load = flags.load("FLAGSTEST_");
++ EXPECT_SOME(load);
++
++ EXPECT_EQ("billy joel", flags.name1);
++ EXPECT_EQ(43, flags.name2);
++ EXPECT_FALSE(flags.name3);
++ ASSERT_SOME(flags.name4);
++ EXPECT_FALSE(flags.name4.get());
++ ASSERT_SOME(flags.name5);
++ EXPECT_TRUE(flags.name5.get());
++
++ os::unsetenv("FLAGSTEST_name1");
++ os::unsetenv("FLAGSTEST_name2");
++ os::unsetenv("FLAGSTEST_no-name3");
++ os::unsetenv("FLAGSTEST_no-name4");
++ os::unsetenv("FLAGSTEST_name5");
++}
++
++
++TEST(FlagsTest, LoadFromCommandLine)
++{
++ TestFlags flags;
++
++ int argc = 6;
++ char* argv[argc];
++
++ argv[0] = (char*) "/path/to/program";
++ argv[1] = (char*) "--name1=billy joel";
++ argv[2] = (char*) "--name2=43";
++ argv[3] = (char*) "--no-name3";
++ argv[4] = (char*) "--no-name4";
++ argv[5] = (char*) "--name5";
++
++ Try<Nothing> load = flags.load("FLAGSTEST_", argc, argv);
++ EXPECT_SOME(load);
++
++ EXPECT_EQ("billy joel", flags.name1);
++ EXPECT_EQ(43, flags.name2);
++ EXPECT_FALSE(flags.name3);
++ ASSERT_SOME(flags.name4);
++ EXPECT_FALSE(flags.name4.get());
++ ASSERT_SOME(flags.name5);
++ EXPECT_TRUE(flags.name5.get());
++}
++
++
++TEST(FlagsTest, LoadFromCommandLineWithNonFlags)
++{
++ TestFlags flags;
++
++ int argc = 11;
++ char* argv[argc];
++
++ argv[0] = (char*) "/path/to/program";
++ argv[1] = (char*) "more";
++ argv[2] = (char*) "--name1=billy joel";
++ argv[3] = (char*) "stuff";
++ argv[4] = (char*) "at";
++ argv[5] = (char*) "--name2=43";
++ argv[6] = (char*) "--no-name3";
++ argv[7] = (char*) "--no-name4";
++ argv[8] = (char*) "--name5";
++ argv[9] = (char*) "the";
++ argv[10] = (char*) "end";
++
++ Try<Nothing> load = flags.load("FLAGSTEST_", argc, argv);
++ EXPECT_SOME(load);
++
++ EXPECT_EQ("billy joel", flags.name1);
++ EXPECT_EQ(43, flags.name2);
++ EXPECT_FALSE(flags.name3);
++ ASSERT_SOME(flags.name4);
++ EXPECT_FALSE(flags.name4.get());
++ ASSERT_SOME(flags.name5);
++ EXPECT_TRUE(flags.name5.get());
++}
++
++
++TEST(FlagsTest, DuplicatesFromEnvironment)
++{
++ TestFlags flags;
++
++ os::setenv("FLAGSTEST_name1", "ben folds");
++
++ int argc = 2;
++ char* argv[argc];
++
++ argv[0] = (char*) "/path/to/program";
++ argv[1] = (char*) "--name1=billy joel";
++
++ Try<Nothing> load = flags.load("FLAGSTEST_", argc, argv);
++ EXPECT_ERROR(load);
++
++ EXPECT_EQ("Duplicate flag 'name1' on command line", load.error());
++
++ os::unsetenv("FLAGSTEST_name1");
++}
++
++
++TEST(FlagsTest, DuplicatesFromCommandLine)
++{
++ TestFlags flags;
++
++ int argc = 3;
++ char* argv[argc];
++
++ argv[0] = (char*) "/path/to/program";
++ argv[1] = (char*) "--name1=billy joel";
++ argv[2] = (char*) "--name1=ben folds";
++
++ Try<Nothing> load = flags.load("FLAGSTEST_", argc, argv);
++ EXPECT_ERROR(load);
++
++ EXPECT_EQ("Duplicate flag 'name1' on command line", load.error());
++}
++
++
++TEST(FlagsTest, Errors)
++{
++ TestFlags flags;
++
++ int argc = 2;
++ char* argv[argc];
++
++ argv[0] = (char*) "/path/to/program";
++
++ // Test an unknown flag.
++ argv[1] = (char*) "--foo";
++
++ Try<Nothing> load = flags.load("FLAGSTEST_", argc, argv);
++ EXPECT_ERROR(load);
++
++ EXPECT_EQ("Failed to load unknown flag 'foo'", load.error());
++
++ // Now try an unknown flag with a value.
++ argv[1] = (char*) "--foo=value";
++
++ load = flags.load("FLAGSTEST_", argc, argv);
++ EXPECT_ERROR(load);
++
++ EXPECT_EQ("Failed to load unknown flag 'foo'", load.error());
++
++ // Now try an unknown flag with a 'no-' prefix.
++ argv[1] = (char*) "--no-foo";
++
++ load = flags.load("FLAGSTEST_", argc, argv);
++ EXPECT_ERROR(load);
++
++ EXPECT_EQ("Failed to load unknown flag 'foo' via 'no-foo'", load.error());
++
++ // Now test a boolean flag using the 'no-' prefix _and_ a value.
++ argv[1] = (char*) "--no-name3=value";
++
++ load = flags.load("FLAGSTEST_", argc, argv);
++ EXPECT_ERROR(load);
++
++ EXPECT_EQ("Failed to load boolean flag 'name3' via "
++ "'no-name3' with value 'value'", load.error());
++
++ // Now test a boolean flag that couldn't be parsed.
++ argv[1] = (char*) "--name3=value";
++
++ load = flags.load("FLAGSTEST_", argc, argv);
++ EXPECT_ERROR(load);
++
++ EXPECT_EQ("Failed to load flag 'name3': Failed to load value 'value': "
++ "Expecting a boolean (e.g., true or false)", load.error());
++
++ // Now test a non-boolean flag without a value.
++ argv[1] = (char*) "--name1";
++
++ load = flags.load("FLAGSTEST_", argc, argv);
++ EXPECT_ERROR(load);
++
++ EXPECT_EQ("Failed to load non-boolean flag 'name1': "
++ "Missing value", load.error());
++
++ // Now test a non-boolean flag using the 'no-' prefix.
++ argv[1] = (char*) "--no-name2";
++
++ load = flags.load("FLAGSTEST_", argc, argv);
++ EXPECT_ERROR(load);
++
++ EXPECT_EQ("Failed to load non-boolean flag 'name2' "
++ "via 'no-name2'", load.error());
++}
++
++
++TEST(FlagsTest, Usage)
++{
++ TestFlags flags;
++
++ EXPECT_EQ(
++ " --name1=VALUE Set name1 (default: ben folds)\n"
++ " --name2=VALUE Set name2 (default: 42)\n"
++ " --[no-]name3 Set name3 (default: false)\n"
++ " --[no-]name4 Set name4\n"
++ " --[no-]name5 Set name5\n",
++ flags.usage());
++}
++
++
++TEST(FlagsTest, Duration)
++{
++ Flags<TestFlags> flags;
++
++ Duration name6;
++
++ flags.add(&name6,
++ "name6",
++ "Amount of time",
++ Milliseconds(100));
++
++ Option<Duration> name7;
++
++ flags.add(&name7,
++ "name7",
++ "Also some amount of time");
++
++ std::map<std::string, Option<std::string> > values;
++
++ values["name6"] = Some("2mins");
++ values["name7"] = Some("3hrs");
++
++ flags.load(values);
++
++ EXPECT_EQ(Minutes(2), name6);
++
++ ASSERT_SOME(name7);
++ EXPECT_EQ(Hours(3), name7.get());
++}
+diff --git a/src/tests/stout/gzip_tests.cpp b/src/tests/stout/gzip_tests.cpp
+new file mode 100644
+index 0000000..13296d8
+--- /dev/null
++++ b/src/tests/stout/gzip_tests.cpp
+@@ -0,0 +1,53 @@
++#include <gtest/gtest.h>
++
++#include <gmock/gmock.h>
++
++#include <string>
++
++#include <stout/gtest.hpp>
++#include <stout/gzip.hpp>
++
++using std::string;
++
++
++#ifdef HAVE_LIBZ
++TEST(GzipTest, CompressDecompressString)
++{
++ // Test bad compression levels, outside of [-1, Z_BEST_COMPRESSION].
++ ASSERT_ERROR(gzip::compress("", -2));
++ ASSERT_ERROR(gzip::compress("", Z_BEST_COMPRESSION + 1));
++
++ string s =
++ "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
++ "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad "
++ "minim veniam, quis nostrud exercitation ullamco laboris nisi ut "
++ "aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit "
++ "in voluptate velit esse cillum dolore eu fugiat nulla pariatur. "
++ "Excepteur sint occaecat cupidatat non proident, sunt in culpa qui "
++ "officia deserunt mollit anim id est laborum.";
++
++ Try<string> compressed = gzip::compress(s);
++ ASSERT_SOME(compressed);
++ Try<string> decompressed = gzip::decompress(compressed.get());
++ ASSERT_SOME(decompressed);
++ ASSERT_EQ(s, decompressed.get());
++
++ // Test with a 1MB random string!
++ s = "";
++ while (s.length() < (1024 * 1024)) {
++ s.append(1, ' ' + (rand() % ('~' - ' ')));
++ }
++ compressed = gzip::compress(s);
++ ASSERT_SOME(compressed);
++ decompressed = gzip::decompress(compressed.get());
++ ASSERT_SOME(decompressed);
++ ASSERT_EQ(s, decompressed.get());
++
++ s = "";
++ compressed = gzip::compress(s);
++ ASSERT_SOME(compressed);
++ decompressed = gzip::decompress(compressed.get());
++ ASSERT_SOME(decompressed);
++ ASSERT_EQ(s, decompressed.get());
++}
++#endif // HAVE_LIBZ
+diff --git a/src/tests/stout/hashmap_tests.cpp b/src/tests/stout/hashmap_tests.cpp
+new file mode 100644
+index 0000000..ff8bafb
+--- /dev/null
++++ b/src/tests/stout/hashmap_tests.cpp
+@@ -0,0 +1,25 @@
++#include <gtest/gtest.h>
++
++#include <gmock/gmock.h>
++
++#include <string>
++
++#include <stout/gtest.hpp>
++#include <stout/hashmap.hpp>
++
++using std::string;
++
++
++TEST(HashMapTest, Insert)
++{
++ hashmap<string, int> map;
++ map["abc"] = 1;
++ map.put("def", 2);
++
++ ASSERT_SOME_EQ(1, map.get("abc"));
++ ASSERT_SOME_EQ(2, map.get("def"));
++
++ map.put("def", 4);
++ ASSERT_SOME_EQ(4, map.get("def"));
++ ASSERT_EQ(2, map.size());
++}
+diff --git a/src/tests/stout/hashset_tests.cpp b/src/tests/stout/hashset_tests.cpp
+new file mode 100644
+index 0000000..3c4b732
+--- /dev/null
++++ b/src/tests/stout/hashset_tests.cpp
+@@ -0,0 +1,48 @@
++#include <gtest/gtest.h>
++
++#include <gmock/gmock.h>
++
++#include <string>
++
++#include <stout/hashset.hpp>
++
++using std::string;
++
++
++TEST(HashsetTest, Insert)
++{
++ hashset<string> hs1;
++ hs1.insert(string("HS1"));
++ hs1.insert(string("HS3"));
++
++ hashset<string> hs2;
++ hs2.insert(string("HS2"));
++
++ hs1 = hs2;
++ ASSERT_EQ(1u, hs1.size());
++ ASSERT_TRUE(hs1.contains("HS2"));
++ ASSERT_TRUE(hs1 == hs2);
++}
++
++
++TEST(HashsetTest, Union)
++{
++ hashset<int> hs1;
++ hs1.insert(1);
++ hs1.insert(2);
++ hs1.insert(3);
++
++ hashset<int> hs2;
++ hs2.insert(3);
++ hs2.insert(4);
++ hs2.insert(5);
++
++ hashset<int> hs3 = hs1 | hs2;
++
++ ASSERT_EQ(5u, hs3.size());
++ ASSERT_TRUE(hs3.contains(1));
++ ASSERT_TRUE(hs3.contains(2));
++ ASSERT_TRUE(hs3.contains(3));
++ ASSERT_TRUE(hs3.contains(4));
++ ASSERT_TRUE(hs3.contains(5));
++}
+diff --git a/src/tests/stout/json_tests.cpp b/src/tests/stout/json_tests.cpp
+new file mode 100644
+index 0000000..29ada8a
+--- /dev/null
++++ b/src/tests/stout/json_tests.cpp
+@@ -0,0 +1,33 @@
++#include <gtest/gtest.h>
++
++#include <gmock/gmock.h>
++
++#include <string>
++
++#include <stout/json.hpp>
++#include <stout/stringify.hpp>
++
++using std::string;
++
++
++TEST(JsonTest, BinaryData)
++{
++ JSON::String s(string("\"\\/\b\f\n\r\t\x00\x19 !#[]\x7F\xFF", 17));
++
++ EXPECT_EQ("\"\\\"\\\\\\/\\b\\f\\n\\r\\t\\u0000\\u0019 !#[]\\u007F\\u00FF\"",
++ stringify(s));
++}
++
++
++TEST(JsonTest, NumberFormat)
++{
++ // Test whole numbers.
++ EXPECT_EQ("0", stringify(JSON::Number(0.0)));
++ EXPECT_EQ("1", stringify(JSON::Number(1.0)));
++
++ // Negative.
++ EXPECT_EQ("-1", stringify(JSON::Number(-1.0)));
++
++ // Expect at least 15 digits of precision.
++ EXPECT_EQ("1234567890.12345", stringify(JSON::Number(1234567890.12345)));
++}
+diff --git a/src/tests/stout/linkedhashmap_tests.cpp b/src/tests/stout/linkedhashmap_tests.cpp
+new file mode 100644
+index 0000000..aca97ca
+--- /dev/null
++++ b/src/tests/stout/linkedhashmap_tests.cpp
+@@ -0,0 +1,93 @@
++#include <stdint.h>
++
++#include <gtest/gtest.h>
++
++#include <list>
++#include <string>
++
++#include <stout/gtest.hpp>
++#include <stout/linkedhashmap.hpp>
++
++using std::list;
++using std::string;
++
++TEST(LinkedHashmapTest, Put)
++{
++ LinkedHashMap<string, int> map;
++
++ map["foo"] = 1;
++ ASSERT_SOME_EQ(1, map.get("foo"));
++ ASSERT_EQ(1, map.size());
++
++ map["bar"] = 2;
++ ASSERT_SOME_EQ(2, map.get("bar"));
++ ASSERT_EQ(2, map.size());
++
++ map["foo"] = 3;
++ ASSERT_SOME_EQ(3, map.get("foo"));
++ ASSERT_EQ(2, map.size());
++}
++
++
++TEST(LinkedHashmapTest, Contains)
++{
++ LinkedHashMap<string, int> map;
++ map["foo"] = 1;
++ map["bar"] = 2;
++ ASSERT_TRUE(map.contains("foo"));
++ ASSERT_TRUE(map.contains("bar"));
++ ASSERT_FALSE(map.contains("caz"));
++}
++
++
++TEST(LinkedHashmapTest, Erase)
++{
++ LinkedHashMap<string, int> map;
++
++ map["foo"] = 1;
++ map["bar"] = 2;
++ ASSERT_EQ(2, map.size());
++
++ ASSERT_EQ(1, map.erase("foo"));
++ ASSERT_EQ(0, map.erase("caz")); // Non-existent key.
++ ASSERT_NONE(map.get("foo"));
++ ASSERT_EQ(1, map.size());
++ ASSERT_SOME_EQ(2, map.get("bar"));
++}
++
++
++TEST(LinkedHashmapTest, Keys)
++{
++ LinkedHashMap<string, int> map;
++
++ std::list<string> keys;
++ keys.push_back("foo");
++ keys.push_back("bar");
++ keys.push_back("food");
++ keys.push_back("rad");
++ keys.push_back("cat");
++
++ // Insert keys into the map.
++ foreach (const string& key, keys) {
++ map[key] = 1;
++ }
++ map["foo"] = 1; // Re-insert a key.
++
++ // Ensure the keys returned are the same as insertion order.
++ ASSERT_EQ(keys, map.keys());
++}
++
++
++TEST(LinkedHashmapTest, Values)
++{
++ LinkedHashMap<string, int> map;
++
++ map["foo"] = 1;
++ map["bar"] = 2;
++ map["caz"] = 3;
++
++ int val = 0;
++ foreach (int value, map.values()) {
++ ASSERT_EQ(++val, value);
++ }
++}
+diff --git a/src/tests/stout/main.cpp b/src/tests/stout/main.cpp
+new file mode 100644
+index 0000000..0f1e9cb
+--- /dev/null
++++ b/src/tests/stout/main.cpp
+@@ -0,0 +1,11 @@
++#include <gtest/gtest.h>
++
++#include <gmock/gmock.h>
++
++int main(int argc, char** argv)
++{
++ // Initialize Google Mock/Test.
++ testing::InitGoogleMock(&argc, argv);
++
++ return RUN_ALL_TESTS();
++}
+diff --git a/src/tests/stout/multimap_tests.cpp b/src/tests/stout/multimap_tests.cpp
+new file mode 100644
+index 0000000..79e7200
+--- /dev/null
++++ b/src/tests/stout/multimap_tests.cpp
+@@ -0,0 +1,168 @@
++#include <stdint.h>
++
++#include <gtest/gtest.h>
++
++#include <set>
++#include <string>
++
++#include <stout/foreach.hpp>
++#include <stout/multimap.hpp>
++#include <stout/multihashmap.hpp>
++
++using std::set;
++using std::string;
++
++template <typename T>
++class MultimapTest : public ::testing::Test {};
++
++typedef ::testing::Types<
++ Multimap<string, uint16_t>, multihashmap<string, uint16_t> > MultimapTypes;
++
++// Causes all TYPED_TEST(MultimapTest, ...) to be run for each of the
++// specified multimap types.
++TYPED_TEST_CASE(MultimapTest, MultimapTypes);
++
++
++TYPED_TEST(MultimapTest, Put)
++{
++ typedef TypeParam Map;
++
++ Map map;
++
++ map.put("foo", 1024);
++ ASSERT_EQ(1u, map.get("foo").size());
++
++ map.put("foo", 1025);
++ ASSERT_EQ(2u, map.get("foo").size());
++
++ ASSERT_EQ(2u, map.size());
++
++ map.put("bar", 1024);
++ ASSERT_EQ(1u, map.get("bar").size());
++
++ map.put("bar", 1025);
++ ASSERT_EQ(2u, map.get("bar").size());
++
++ ASSERT_EQ(4u, map.size());
++}
++
++
++TYPED_TEST(MultimapTest, Remove)
++{
++ typedef TypeParam Map;
++
++ Map map;
++
++ map.put("foo", 1024);
++ map.remove("foo", 1024);
++ ASSERT_EQ(0u, map.get("foo").size());
++
++ ASSERT_EQ(0u, map.size());
++
++ map.put("foo", 1024);
++ map.put("foo", 1025);
++ ASSERT_EQ(2u, map.get("foo").size());
++
++ ASSERT_EQ(2u, map.size());
++
++ map.remove("foo");
++ ASSERT_EQ(0u, map.get("foo").size());
++ ASSERT_EQ(0u, map.size());
++}
++
++
++TYPED_TEST(MultimapTest, Size)
++{
++ typedef TypeParam Map;
++
++ Map map;
++
++ map.put("foo", 1024);
++ map.put("foo", 1025);
++ ASSERT_EQ(2u, map.get("foo").size());
++ ASSERT_TRUE(map.contains("foo", 1024));
++ ASSERT_TRUE(map.contains("foo", 1025));
++ ASSERT_EQ(2u, map.size());
++
++ map.put("bar", 1024);
++ map.put("bar", 1025);
++ ASSERT_EQ(2u, map.get("bar").size());
++ ASSERT_TRUE(map.contains("bar", 1024));
++ ASSERT_TRUE(map.contains("bar", 1025));
++ ASSERT_EQ(4u, map.size());
++}
++
++
++TYPED_TEST(MultimapTest, Keys)
++{
++ typedef TypeParam Map;
++
++ Map map;
++
++ map.put("foo", 1024);
++ map.put("foo", 1024);
++ map.put("foo", 1024);
++ map.put("foo", 1025);
++ map.put("bar", 1);
++
++ set<string> keys = map.keys();
++
++ ASSERT_EQ(2, keys.size());
++ ASSERT_EQ(1, keys.count("foo"));
++ ASSERT_EQ(1, keys.count("bar"));
++}
++
++
++TYPED_TEST(MultimapTest, Iterator)
++{
++ typedef TypeParam Map;
++
++ Map map;
++
++ map.put("foo", 1024);
++ map.put("foo", 1025);
++ ASSERT_EQ(2u, map.get("foo").size());
++ ASSERT_TRUE(map.contains("foo", 1024));
++ ASSERT_TRUE(map.contains("foo", 1025));
++
++ typename Map::iterator i = map.begin();
++
++ ASSERT_TRUE(i != map.end());
++
++ ASSERT_EQ("foo", i->first);
++ ASSERT_EQ(1024, i->second);
++
++ ++i;
++ ASSERT_TRUE(i != map.end());
++
++ ASSERT_EQ("foo", i->first);
++ ASSERT_EQ(1025, i->second);
++
++ ++i;
++ ASSERT_TRUE(i == map.end());
++}
++
++
++TYPED_TEST(MultimapTest, Foreach)
++{
++ typedef TypeParam Map;
++
++ Map map;
++
++ map.put("foo", 1024);
++ map.put("bar", 1025);
++ ASSERT_EQ(1u, map.get("foo").size());
++ ASSERT_EQ(1u, map.get("bar").size());
++ ASSERT_TRUE(map.contains("foo", 1024));
++ ASSERT_TRUE(map.contains("bar", 1025));
++
++ foreachpair (const string& key, uint16_t value, map) {
++ if (key == "foo") {
++ ASSERT_EQ(1024, value);
++ } else if (key == "bar") {
++ ASSERT_EQ(1025, value);
++ } else {
++ FAIL() << "Unexpected key/value in multimap";
++ }
++ }
++}
+diff --git a/src/tests/stout/none_tests.cpp b/src/tests/stout/none_tests.cpp
+new file mode 100644
+index 0000000..38d25bb
+--- /dev/null
++++ b/src/tests/stout/none_tests.cpp
+@@ -0,0 +1,59 @@
++#include <gtest/gtest.h>
++
++#include <gmock/gmock.h>
++
++#include <string>
++
++#include <stout/none.hpp>
++#include <stout/option.hpp>
++#include <stout/result.hpp>
++
++using std::string;
++
++
++None none1()
++{
++ return None();
++}
++
++
++Option<string> none2()
++{
++ return None();
++}
++
++
++Option<string> none3(const Option<string>& o)
++{
++ return o;
++}
++
++
++Result<string> none4()
++{
++ return None();
++}
++
++
++Result<string> none5(const Result<string>& r)
++{
++ return r;
++}
++
++
++TEST(NoneTest, Test)
++{
++ Option<string> o = none1();
++ EXPECT_TRUE(o.isNone());
++ o = none2();
++ EXPECT_TRUE(o.isNone());
++ o = none3(none1());
++ EXPECT_TRUE(o.isNone());
++
++ Result<string> r = none1();
++ EXPECT_TRUE(r.isNone());
++ r = none4();
++ EXPECT_TRUE(r.isNone());
++ r = none5(none1());
++ EXPECT_TRUE(r.isNone());
++}
+diff --git a/src/tests/stout/os/sendfile_tests.cpp b/src/tests/stout/os/sendfile_tests.cpp
+new file mode 100644
+index 0000000..194906e
+--- /dev/null
++++ b/src/tests/stout/os/sendfile_tests.cpp
+@@ -0,0 +1,84 @@
++#include <gmock/gmock.h>
++
++#include <gtest/gtest.h>
++
++#include <stout/gtest.hpp>
++#include <stout/os.hpp>
++#include <stout/path.hpp>
++
++using std::string;
++
++// TODO(bmahler): Extend from OsTest.
++class OsSendfileTest : public ::testing::Test
++{
++public:
++ OsSendfileTest()
++ : LOREM_IPSUM(
++ "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
++ "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim "
++ "ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut "
++ "aliquip ex ea commodo consequat. Duis aute irure dolor in "
++ "reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla "
++ "pariatur. Excepteur sint occaecat cupidatat non proident, sunt in "
++ "culpa qui officia deserunt mollit anim id est laborum.") {}
++
++protected:
++ virtual void SetUp()
++ {
++ const Try<string>& mkdtemp = os::mkdtemp();
++ ASSERT_SOME(mkdtemp);
++ tmpdir = mkdtemp.get();
++ filename = path::join(mkdtemp.get(), "lorem.txt");
++
++ ASSERT_SOME(os::write(filename, LOREM_IPSUM));
++ }
++
++ virtual void TearDown()
++ {
++ ASSERT_SOME(os::rmdir(tmpdir));
++ }
++
++ const string LOREM_IPSUM;
++ string filename;
++
++private:
++ string tmpdir;
++};
++
++
++TEST_F(OsSendfileTest, sendfile)
++{
++ Try<int> fd = os::open(filename, O_RDONLY);
++ ASSERT_SOME(fd);
++
++ // Construct a socket pair and use sendfile to transmit the text.
++ int s[2];
++ ASSERT_NE(-1, socketpair(AF_UNIX, SOCK_STREAM, 0, s)) << strerror(errno);
++ ASSERT_EQ(
++ LOREM_IPSUM.size(),
++ os::sendfile(s[0], fd.get(), 0, LOREM_IPSUM.size()));
++
++ char* buffer = new char[LOREM_IPSUM.size()];
++ ASSERT_EQ(LOREM_IPSUM.size(), read(s[1], buffer, LOREM_IPSUM.size()));
++ ASSERT_EQ(LOREM_IPSUM, string(buffer, LOREM_IPSUM.size()));
++ ASSERT_SOME(os::close(fd.get()));
++ delete buffer;
++
++ // Now test with a closed socket, the SIGPIPE should be suppressed!
++ fd = os::open(filename, O_RDONLY);
++ ASSERT_SOME(fd);
++ ASSERT_SOME(os::close(s[1]));
++
++ ssize_t result = os::sendfile(s[0], fd.get(), 0, LOREM_IPSUM.size());
++ int _errno = errno;
++ ASSERT_EQ(-1, result);
++
++#ifdef __linux__
++ ASSERT_EQ(EPIPE, _errno) << strerror(_errno);
++#elif defined __APPLE__
++ ASSERT_EQ(ENOTCONN, _errno) << strerror(_errno);
++#endif
++
++ ASSERT_SOME(os::close(fd.get()));
++ ASSERT_SOME(os::close(s[0]));
++}
+diff --git a/src/tests/stout/os/signals_tests.cpp b/src/tests/stout/os/signals_tests.cpp
+new file mode 100644
+index 0000000..66caa04
+--- /dev/null
++++ b/src/tests/stout/os/signals_tests.cpp
+@@ -0,0 +1,34 @@
++#include <errno.h>
++
++#include <gmock/gmock.h>
++
++#include <gtest/gtest.h>
++
++#include <stout/gtest.hpp>
++#include <stout/os.hpp>
++
++using std::string;
++
++// TODO(bmahler): Expose OsTest so this can use it.
++class OsSignalsTest : public ::testing::Test {};
++
++
++TEST_F(OsSignalsTest, suppress)
++{
++ int pipes[2];
++ ASSERT_NE(-1, pipe(pipes));
++
++ ASSERT_SOME(os::close(pipes[0]));
++
++ const string data = "hello";
++
++ // Let's make sure we can suppress SIGPIPE!
++ suppress(SIGPIPE) {
++ // Writing to a pipe that has been closed generates SIGPIPE.
++ ASSERT_EQ(-1, write(pipes[1], data.c_str(), data.length()));
++
++ ASSERT_EQ(EPIPE, errno);
++ }
++
++ ASSERT_SOME(os::close(pipes[1]));
++}
+diff --git a/src/tests/stout/os_tests.cpp b/src/tests/stout/os_tests.cpp
+new file mode 100644
+index 0000000..a0b624b
+--- /dev/null
++++ b/src/tests/stout/os_tests.cpp
+@@ -0,0 +1,533 @@
++#include <gmock/gmock.h>
++
++#include <gtest/gtest.h>
++
++#include <cstdlib> // For rand.
++#include <list>
++#include <set>
++#include <string>
++
++#include <stout/duration.hpp>
++#include <stout/foreach.hpp>
++#include <stout/gtest.hpp>
++#include <stout/hashset.hpp>
++#include <stout/os.hpp>
++#include <stout/stopwatch.hpp>
++#include <stout/try.hpp>
++#include <stout/uuid.hpp>
++
++#ifdef __APPLE__
++#include <stout/os/sysctl.hpp>
++#endif
++
++using os::Exec;
++using os::Fork;
++using os::Process;
++using os::ProcessTree;
++
++using std::list;
++using std::set;
++using std::string;
++
++
++static hashset<string> listfiles(const string& directory)
++{
++ hashset<string> fileset;
++ foreach (const string& file, os::ls(directory)) {
++ fileset.insert(file);
++ }
++ return fileset;
++}
++
++
++class OsTest : public ::testing::Test
++{
++protected:
++ virtual void SetUp()
++ {
++ const Try<string>& mkdtemp = os::mkdtemp();
++ ASSERT_SOME(mkdtemp);
++ tmpdir = mkdtemp.get();
++ }
++
++ virtual void TearDown()
++ {
++ ASSERT_SOME(os::rmdir(tmpdir));
++ }
++
++ string tmpdir;
++};
++
++
++TEST_F(OsTest, rmdir)
++{
++ const hashset<string> EMPTY;
++
++ hashset<string> expectedListing = EMPTY;
++ EXPECT_EQ(expectedListing, listfiles(tmpdir));
++
++ os::mkdir(tmpdir + "/a/b/c");
++ os::mkdir(tmpdir + "/a/b/d");
++ os::mkdir(tmpdir + "/e/f");
++
++ expectedListing = EMPTY;
++ expectedListing.insert("a");
++ expectedListing.insert("e");
++ EXPECT_EQ(expectedListing, listfiles(tmpdir));
++
++ expectedListing = EMPTY;
++ expectedListing.insert("b");
++ EXPECT_EQ(expectedListing, listfiles(tmpdir + "/a"));
++
++ expectedListing = EMPTY;
++ expectedListing.insert("c");
++ expectedListing.insert("d");
++ EXPECT_EQ(expectedListing, listfiles(tmpdir + "/a/b"));
++
++ expectedListing = EMPTY;
++ EXPECT_EQ(expectedListing, listfiles(tmpdir + "/a/b/c"));
++ EXPECT_EQ(expectedListing, listfiles(tmpdir + "/a/b/d"));
++
++ expectedListing.insert("f");
++ EXPECT_EQ(expectedListing, listfiles(tmpdir + "/e"));
++
++ expectedListing = EMPTY;
++ EXPECT_EQ(expectedListing, listfiles(tmpdir + "/e/f"));
++}
++
++
++TEST_F(OsTest, nonblock)
++{
++ int pipes[2];
++ ASSERT_NE(-1, pipe(pipes));
++
++ Try<bool> isNonBlock = false;
++
++ isNonBlock = os::isNonblock(pipes[0]);
++ ASSERT_SOME(isNonBlock);
++ EXPECT_FALSE(isNonBlock.get());
++
++ ASSERT_SOME(os::nonblock(pipes[0]));
++
++ isNonBlock = os::isNonblock(pipes[0]);
++ ASSERT_SOME(isNonBlock);
++ EXPECT_TRUE(isNonBlock.get());
++
++ close(pipes[0]);
++ close(pipes[1]);
++
++ EXPECT_ERROR(os::nonblock(pipes[0]));
++ EXPECT_ERROR(os::nonblock(pipes[0]));
++}
++
++
++TEST_F(OsTest, touch)
++{
++ const string& testfile = tmpdir + "/" + UUID::random().toString();
++
++ ASSERT_SOME(os::touch(testfile));
++ ASSERT_TRUE(os::exists(testfile));
++}
++
++
++TEST_F(OsTest, readWriteString)
++{
++ const string& testfile = tmpdir + "/" + UUID::random().toString();
++ const string& teststr = "line1\nline2";
++
++ ASSERT_SOME(os::write(testfile, teststr));
++
++ Try<string> readstr = os::read(testfile);
++
++ ASSERT_SOME(readstr);
++ EXPECT_EQ(teststr, readstr.get());
++}
++
++
++TEST_F(OsTest, find)
++{
++ const string& testdir = tmpdir + "/" + UUID::random().toString();
++ const string& subdir = testdir + "/test1";
++ ASSERT_SOME(os::mkdir(subdir)); // Create the directories.
++
++ // Now write some files.
++ const string& file1 = testdir + "/file1.txt";
++ const string& file2 = subdir + "/file2.txt";
++ const string& file3 = subdir + "/file3.jpg";
++
++ ASSERT_SOME(os::touch(file1));
++ ASSERT_SOME(os::touch(file2));
++ ASSERT_SOME(os::touch(file3));
++
++ // Find "*.txt" files.
++ Try<std::list<string> > result = os::find(testdir, ".txt");
++ ASSERT_SOME(result);
++
++ hashset<string> files;
++ foreach (const string& file, result.get()) {
++ files.insert(file);
++ }
++
++ ASSERT_EQ(2u, files.size());
++ ASSERT_TRUE(files.contains(file1));
++ ASSERT_TRUE(files.contains(file2));
++}
++
++
++TEST_F(OsTest, uname)
++{
++ const Try<os::UTSInfo>& info = os::uname();
++
++ ASSERT_SOME(info);
++#ifdef __linux__
++ EXPECT_EQ(info.get().sysname, "Linux");
++#endif
++#ifdef __APPLE__
++ EXPECT_EQ(info.get().sysname, "Darwin");
++#endif
++}
++
++
++TEST_F(OsTest, sysname)
++{
++ const Try<string>& name = os::sysname();
++
++ ASSERT_SOME(name);
++#ifdef __linux__
++ EXPECT_EQ(name.get(), "Linux");
++#endif
++#ifdef __APPLE__
++ EXPECT_EQ(name.get(), "Darwin");
++#endif
++}
++
++
++TEST_F(OsTest, release)
++{
++ const Try<os::Release>& info = os::release();
++
++ ASSERT_SOME(info);
++}
++
++
++TEST_F(OsTest, sleep)
++{
++ Duration duration = Milliseconds(10);
++ Stopwatch stopwatch;
++ stopwatch.start();
++ ASSERT_SOME(os::sleep(duration));
++ ASSERT_LE(duration, stopwatch.elapsed());
++
++ ASSERT_ERROR(os::sleep(Milliseconds(-10)));
++}
++
++
++#ifdef __APPLE__
++TEST_F(OsTest, sysctl)
++{
++ Try<os::UTSInfo> uname = os::uname();
++
++ ASSERT_SOME(uname);
++
++ Try<string> release = os::sysctl(CTL_KERN, KERN_OSRELEASE).string();
++
++ ASSERT_SOME(release);
++ EXPECT_EQ(uname.get().release, release.get());
++
++ Try<string> type = os::sysctl(CTL_KERN, KERN_OSTYPE).string();
++
++ ASSERT_SOME(type);
++ EXPECT_EQ(uname.get().sysname, type.get());
++
++ Try<int> maxproc = os::sysctl(CTL_KERN, KERN_MAXPROC).integer();
++
++ ASSERT_SOME(maxproc);
++
++ Try<std::vector<kinfo_proc> > processes =
++ os::sysctl(CTL_KERN, KERN_PROC, KERN_PROC_ALL).table(maxproc.get());
++
++ ASSERT_SOME(processes);
++
++ std::set<pid_t> pids;
++
++ foreach (const kinfo_proc& process, processes.get()) {
++ pids.insert(process.kp_proc.p_pid);
++ }
++
++ EXPECT_EQ(1, pids.count(getpid()));
++}
++#endif // __APPLE__
++
++
++TEST_F(OsTest, pids)
++{
++ Try<set<pid_t> > pids = os::pids();
++ ASSERT_SOME(pids);
++ EXPECT_NE(0u, pids.get().size());
++ EXPECT_EQ(1u, pids.get().count(getpid()));
++ EXPECT_EQ(1u, pids.get().count(1));
++
++ pids = os::pids(getpgid(0), None());
++ EXPECT_SOME(pids);
++ EXPECT_GE(pids.get().size(), 1u);
++ EXPECT_EQ(1u, pids.get().count(getpid()));
++
++ EXPECT_ERROR(os::pids(-1, None()));
++
++ pids = os::pids(None(), getsid(0));
++ EXPECT_SOME(pids);
++ EXPECT_GE(pids.get().size(), 1u);
++ EXPECT_EQ(1u, pids.get().count(getpid()));
++
++ EXPECT_ERROR(os::pids(None(), -1));
++}
++
++
++TEST_F(OsTest, children)
++{
++ Try<set<pid_t> > children = os::children(getpid());
++
++ ASSERT_SOME(children);
++ EXPECT_EQ(0u, children.get().size());
++
++ Try<ProcessTree> tree =
++ Fork(None(), // Child.
++ Fork(Exec("sleep 10")), // Grandchild.
++ Exec("sleep 10"))();
++
++ ASSERT_SOME(tree);
++ ASSERT_EQ(1u, tree.get().children.size());
++
++ pid_t child = tree.get().process.pid;
++ pid_t grandchild = tree.get().children.front().process.pid;
++
++ // Ensure the non-recursive children does not include the
++ // grandchild.
++ children = os::children(getpid(), false);
++
++ ASSERT_SOME(children);
++ EXPECT_EQ(1u, children.get().size());
++ EXPECT_EQ(1u, children.get().count(child));
++
++ children = os::children(getpid());
++
++ ASSERT_SOME(children);
++
++ // Depending on whether or not the shell has fork/exec'ed in each
++ // above 'Exec', we could have 2 or 4 children. That is, some shells
++ // might simply for exec the command above (i.e., 'sleep 10') while
++ // others might fork/exec the command, keeping around a 'sh -c'
++ // process as well.
++ EXPECT_LE(2u, children.get().size());
++ EXPECT_GE(4u, children.get().size());
++
++ EXPECT_EQ(1u, children.get().count(child));
++ EXPECT_EQ(1u, children.get().count(grandchild));
++
++ // Cleanup by killing the descendant processes.
++ EXPECT_EQ(0, kill(grandchild, SIGKILL));
++ EXPECT_EQ(0, kill(child, SIGKILL));
++
++ // We have to reap the child for running the tests in repetition.
++ ASSERT_EQ(child, waitpid(child, NULL, 0));
++}
++
++
++TEST_F(OsTest, process)
++{
++ const Result<Process>& process = os::process(getpid());
++
++ ASSERT_SOME(process);
++ EXPECT_EQ(getpid(), process.get().pid);
++ EXPECT_EQ(getppid(), process.get().parent);
++ ASSERT_SOME(process.get().session);
++ EXPECT_EQ(getsid(getpid()), process.get().session.get());
++
++ ASSERT_SOME(process.get().rss);
++ EXPECT_GT(process.get().rss.get(), 0);
++
++ // NOTE: On Linux /proc is a bit slow to update the CPU times,
++ // hence we allow 0 in this test.
++ ASSERT_SOME(process.get().utime);
++ EXPECT_GE(process.get().utime.get(), Nanoseconds(0));
++ ASSERT_SOME(process.get().stime);
++ EXPECT_GE(process.get().stime.get(), Nanoseconds(0));
++
++ EXPECT_FALSE(process.get().command.empty());
++}
++
++
++TEST_F(OsTest, processes)
++{
++ const Try<list<Process> >& processes = os::processes();
++
++ ASSERT_SOME(processes);
++ ASSERT_GT(processes.get().size(), 2);
++
++ // Look for ourselves in the table.
++ bool found = false;
++ foreach (const Process& process, processes.get()) {
++ if (process.pid == getpid()) {
++ found = true;
++ EXPECT_EQ(getpid(), process.pid);
++ EXPECT_EQ(getppid(), process.parent);
++ ASSERT_SOME(process.session);
++ EXPECT_EQ(getsid(getpid()), process.session.get());
++
++ ASSERT_SOME(process.rss);
++ EXPECT_GT(process.rss.get(), 0);
++
++ // NOTE: On linux /proc is a bit slow to update the cpu times,
++ // hence we allow 0 in this test.
++ ASSERT_SOME(process.utime);
++ EXPECT_GE(process.utime.get(), Nanoseconds(0));
++ ASSERT_SOME(process.stime);
++ EXPECT_GE(process.stime.get(), Nanoseconds(0));
++
++ EXPECT_FALSE(process.command.empty());
++
++ break;
++ }
++ }
++
++ EXPECT_TRUE(found);
++}
++
++
++void dosetsid(void)
++{
++ if (::setsid() == -1) {
++ perror("Failed to setsid");
++ abort();
++ }
++}
++
++
++TEST_F(OsTest, killtree)
++{
++ Try<ProcessTree> tree =
++ Fork(dosetsid, // Child.
++ Fork(None(), // Grandchild.
++ Fork(None(), // Great-grandchild.
++ Fork(dosetsid, // Great-great-granchild.
++ Exec("sleep 10")),
++ Exec("sleep 10")),
++ Exec("exit 0")),
++ Exec("sleep 10"))();
++
++ ASSERT_SOME(tree);
++
++ // The process tree we instantiate initially looks like this:
++ //
++ // -+- child sleep 10
++ // \-+- grandchild exit 0
++ // \-+- greatGrandchild sleep 10
++ // \--- greatGreatGrandchild sleep 10
++ //
++ // But becomes two process trees after the grandchild exits:
++ //
++ // -+- child sleep 10
++ // \--- grandchild (exit 0)
++ //
++ // -+- greatGrandchild sleep 10
++ // \--- greatGreatGrandchild sleep 10
++
++ // Grab the pids from the instantiated process tree.
++ ASSERT_EQ(1u, tree.get().children.size());
++ ASSERT_EQ(1u, tree.get().children.front().children.size());
++ ASSERT_EQ(1u, tree.get().children.front().children.front().children.size());
++
++ pid_t child = tree.get();
++ pid_t grandchild = tree.get().children.front();
++ pid_t greatGrandchild = tree.get().children.front().children.front();
++ pid_t greatGreatGrandchild =
++ tree.get().children.front().children.front().children.front();
++
++ // Now wait for the grandchild to exit splitting the process tree.
++ os::sleep(Milliseconds(50));
++
++ // Kill the process tree and follow sessions and groups to make sure
++ // we cross the broken link due to the grandchild.
++ Try<std::list<ProcessTree> > trees =
++ os::killtree(child, SIGKILL, true, true);
++
++ ASSERT_SOME(trees);
++
++ EXPECT_EQ(2u, trees.get().size()) << stringify(trees.get());
++
++ foreach (const ProcessTree& tree, trees.get()) {
++ if (tree.process.pid == child) {
++ // The 'grandchild' _might_ still be in the tree, just zombied,
++ // unless the 'child' reaps the 'grandchild', which may happen
++ // if the shell "sticks around" (i.e., some invocations of 'sh
++ // -c' will 'exec' the command which will likely not do any
++ // reaping, but in other cases an invocation of 'sh -c' will not
++ // 'exec' the command, for example when the command is a
++ // sequence of commands separated by ';').
++ EXPECT_FALSE(tree.contains(greatGrandchild)) << tree;
++ EXPECT_FALSE(tree.contains(greatGreatGrandchild)) << tree;
++ } else if (tree.process.pid == greatGrandchild) {
++ EXPECT_TRUE(tree.contains(greatGreatGrandchild)) << tree;
++ } else {
++ FAIL()
++ << "Not expecting a process tree rooted at "
++ << tree.process.pid << "\n" << tree;
++ }
++ }
++
++ // There is a delay for processes to move into the zombie state.
++ os::sleep(Milliseconds(50));
++
++ // Expect the pids to be wiped!
++ EXPECT_NONE(os::process(greatGreatGrandchild));
++ EXPECT_NONE(os::process(greatGrandchild));
++ EXPECT_NONE(os::process(grandchild));
++ EXPECT_SOME(os::process(child));
++ EXPECT_TRUE(os::process(child).get().zombie);
++
++ // We have to reap the child for running the tests in repetition.
++ ASSERT_EQ(child, waitpid(child, NULL, 0));
++}
++
++
++TEST_F(OsTest, pstree)
++{
++ Try<ProcessTree> tree = os::pstree(getpid());
++
++ ASSERT_SOME(tree);
++ EXPECT_EQ(0u, tree.get().children.size()) << stringify(tree.get());
++
++ tree =
++ Fork(None(), // Child.
++ Fork(Exec("sleep 10")), // Grandchild.
++ Exec("sleep 10"))();
++
++ ASSERT_SOME(tree);
++
++ // Depending on whether or not the shell has fork/exec'ed,
++ // we could have 1 or 2 direct children. That is, some shells
++ // might simply exec the command above (i.e., 'sleep 10') while
++ // others might fork/exec the command, keeping around a 'sh -c'
++ // process as well.
++ ASSERT_LE(1u, tree.get().children.size());
++ ASSERT_GE(2u, tree.get().children.size());
++
++ pid_t child = tree.get().process.pid;
++ pid_t grandchild = tree.get().children.front().process.pid;
++
++ // Now check pstree again.
++ tree = os::pstree(child);
++
++ ASSERT_SOME(tree);
++ EXPECT_EQ(child, tree.get().process.pid);
++
++ ASSERT_LE(1u, tree.get().children.size());
++ ASSERT_GE(2u, tree.get().children.size());
++
++ // Cleanup by killing the descendant processes.
++ EXPECT_EQ(0, kill(grandchild, SIGKILL));
++ EXPECT_EQ(0, kill(child, SIGKILL));
++
++ // We have to reap the child for running the tests in repetition.
++ ASSERT_EQ(child, waitpid(child, NULL, 0));
++}
+diff --git a/src/tests/stout/proc_tests.cpp b/src/tests/stout/proc_tests.cpp
+new file mode 100644
+index 0000000..bc7e248
+--- /dev/null
++++ b/src/tests/stout/proc_tests.cpp
+@@ -0,0 +1,54 @@
++#include <unistd.h> // For getpid, getppid.
++
++#include <gmock/gmock.h>
++
++#include <set>
++
++#include <stout/gtest.hpp>
++#include <stout/proc.hpp>
++#include <stout/try.hpp>
++
++using proc::CPU;
++using proc::SystemStatus;
++using proc::ProcessStatus;
++
++using std::set;
++
++
++TEST(ProcTest, pids)
++{
++ Try<set<pid_t> > pids = proc::pids();
++
++ ASSERT_SOME(pids);
++ EXPECT_NE(0u, pids.get().size());
++ EXPECT_EQ(1u, pids.get().count(getpid()));
++ EXPECT_EQ(1u, pids.get().count(1));
++}
++
++
++TEST(ProcTest, cpus)
++{
++ Try<std::list<CPU> > cpus = proc::cpus();
++
++ ASSERT_SOME(cpus);
++ EXPECT_LE(1u, cpus.get().size());
++}
++
++
++TEST(ProcTest, SystemStatus)
++{
++ Try<SystemStatus> status = proc::status();
++
++ ASSERT_SOME(status);
++ EXPECT_NE(0u, status.get().btime);
++}
++
++
++TEST(ProcTest, ProcessStatus)
++{
++ Result<ProcessStatus> status = proc::status(getpid());
++
++ ASSERT_SOME(status);
++ EXPECT_EQ(getpid(), status.get().pid);
++ EXPECT_EQ(getppid(), status.get().ppid);
++}
+diff --git a/src/tests/stout/protobuf_tests.cpp b/src/tests/stout/protobuf_tests.cpp
+new file mode 100644
+index 0000000..02bbf74
+--- /dev/null
++++ b/src/tests/stout/protobuf_tests.cpp
+@@ -0,0 +1,95 @@
++#include <gtest/gtest.h>
++
++#include <gmock/gmock.h>
++
++#include <string>
++
++#include <stout/json.hpp>
++#include <stout/protobuf.hpp>
++#include <stout/stringify.hpp>
++#include <stout/strings.hpp>
++
++#include "protobuf_tests.pb.h"
++
++using std::string;
++
++TEST(ProtobufTest, JSON)
++{
++ tests::Message message;
++ message.set_str("string");
++ message.set_bytes("bytes");
++ message.set_int32(-1);
++ message.set_int64(-1);
++ message.set_uint32(1);
++ message.set_uint64(1);
++ message.set_sint32(-1);
++ message.set_sint64(-1);
++ message.set_f(1.0);
++ message.set_d(1.0);
++ message.set_e(tests::ONE);
++ message.mutable_nested()->set_str("nested");
++ message.add_repeated_string("repeated_string");
++ message.add_repeated_bytes("repeated_bytes");
++ message.add_repeated_int32(-2);
++ message.add_repeated_int64(-2);
++ message.add_repeated_uint32(2);
++ message.add_repeated_uint64(2);
++ message.add_repeated_sint32(-2);
++ message.add_repeated_sint64(-2);
++ message.add_repeated_float(1.0);
++ message.add_repeated_double(1.0);
++ message.add_repeated_double(2.0);
++ message.add_repeated_enum(tests::TWO);
++ message.add_repeated_nested()->set_str("repeated_nested");
++
++ // TODO(bmahler): To dynamically generate a protobuf message,
++ // see the commented-out code below.
++// DescriptorProto proto;
++//
++// proto.set_name("Message");
++//
++// FieldDescriptorProto* field = proto.add_field();
++// field->set_name("str");
++// field->set_type(FieldDescriptorProto::TYPE_STRING);
++//
++// const Descriptor* descriptor = proto.descriptor();
++//
++// DynamicMessageFactory factory;
++// Message* message = factory.GetPrototype(descriptor);
++//
++// Reflection* message.getReflection();
++
++ // The keys are in alphabetical order.
++ string expected = strings::remove(
++ "{"
++ " \"bytes\": \"bytes\","
++ " \"d\": 1,"
++ " \"e\": \"ONE\","
++ " \"f\": 1,"
++ " \"int32\": -1,"
++ " \"int64\": -1,"
++ " \"nested\": { \"str\": \"nested\"},"
++ " \"repeated_bytes\": [\"repeated_bytes\"],"
++ " \"repeated_double\": [1, 2],"
++ " \"repeated_enum\": [\"TWO\"],"
++ " \"repeated_float\": [1],"
++ " \"repeated_int32\": [-2],"
++ " \"repeated_int64\": [-2],"
++ " \"repeated_nested\": [ { \"str\": \"repeated_nested\" } ],"
++ " \"repeated_sint32\": [-2],"
++ " \"repeated_sint64\": [-2],"
++ " \"repeated_string\": [\"repeated_string\"],"
++ " \"repeated_uint32\": [2],"
++ " \"repeated_uint64\": [2],"
++ " \"sint32\": -1,"
++ " \"sint64\": -1,"
++ " \"str\": \"string\","
++ " \"uint32\": 1,"
++ " \"uint64\": 1"
++ "}",
++ " ");
++
++ JSON::Object object = JSON::Protobuf(message);
++
++ EXPECT_EQ(expected, stringify(object));
++}
+diff --git a/src/tests/stout/protobuf_tests.pb.cc b/src/tests/stout/protobuf_tests.pb.cc
+new file mode 100644
+index 0000000..ecf34e1
+--- /dev/null
++++ b/src/tests/stout/protobuf_tests.pb.cc
+@@ -0,0 +1,1692 @@
++// Generated by the protocol buffer compiler. DO NOT EDIT!
++
++#define INTERNAL_SUPPRESS_PROTOBUF_FIELD_DEPRECATION
++#include "protobuf_tests.pb.h"
++
++#include <algorithm>
++
++#include <google/protobuf/stubs/once.h>
++#include <google/protobuf/io/coded_stream.h>
++#include <google/protobuf/wire_format_lite_inl.h>
++#include <google/protobuf/descriptor.h>
++#include <google/protobuf/reflection_ops.h>
++#include <google/protobuf/wire_format.h>
++// @@protoc_insertion_point(includes)
++
++namespace tests {
++
++namespace {
++
++const ::google::protobuf::Descriptor* Nested_descriptor_ = NULL;
++const ::google::protobuf::internal::GeneratedMessageReflection*
++ Nested_reflection_ = NULL;
++const ::google::protobuf::Descriptor* Message_descriptor_ = NULL;
++const ::google::protobuf::internal::GeneratedMessageReflection*
++ Message_reflection_ = NULL;
++const ::google::protobuf::EnumDescriptor* Enum_descriptor_ = NULL;
++
++} // namespace
++
++
++void protobuf_AssignDesc_protobuf_5ftests_2eproto() {
++ protobuf_AddDesc_protobuf_5ftests_2eproto();
++ const ::google::protobuf::FileDescriptor* file =
++ ::google::protobuf::DescriptorPool::generated_pool()->FindFileByName(
++ "protobuf_tests.proto");
++ GOOGLE_CHECK(file != NULL);
++ Nested_descriptor_ = file->message_type(0);
++ static const int Nested_offsets_[1] = {
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Nested, str_),
++ };
++ Nested_reflection_ =
++ new ::google::protobuf::internal::GeneratedMessageReflection(
++ Nested_descriptor_,
++ Nested::default_instance_,
++ Nested_offsets_,
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Nested, _has_bits_[0]),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Nested, _unknown_fields_),
++ -1,
++ ::google::protobuf::DescriptorPool::generated_pool(),
++ ::google::protobuf::MessageFactory::generated_factory(),
++ sizeof(Nested));
++ Message_descriptor_ = file->message_type(1);
++ static const int Message_offsets_[25] = {
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, str_),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, bytes_),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, int32_),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, int64_),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, uint32_),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, uint64_),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, sint32_),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, sint64_),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, f_),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, d_),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, e_),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, nested_),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, repeated_string_),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, repeated_bytes_),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, repeated_int32_),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, repeated_int64_),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, repeated_uint32_),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, repeated_uint64_),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, repeated_sint32_),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, repeated_sint64_),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, repeated_float_),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, repeated_double_),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, repeated_enum_),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, repeated_nested_),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, empty_),
++ };
++ Message_reflection_ =
++ new ::google::protobuf::internal::GeneratedMessageReflection(
++ Message_descriptor_,
++ Message::default_instance_,
++ Message_offsets_,
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, _has_bits_[0]),
++ GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Message, _unknown_fields_),
++ -1,
++ ::google::protobuf::DescriptorPool::generated_pool(),
++ ::google::protobuf::MessageFactory::generated_factory(),
++ sizeof(Message));
++ Enum_descriptor_ = file->enum_type(0);
++}
++
++namespace {
++
++GOOGLE_PROTOBUF_DECLARE_ONCE(protobuf_AssignDescriptors_once_);
++inline void protobuf_AssignDescriptorsOnce() {
++ ::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_,
++ &protobuf_AssignDesc_protobuf_5ftests_2eproto);
++}
++
++void protobuf_RegisterTypes(const ::std::string&) {
++ protobuf_AssignDescriptorsOnce();
++ ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
++ Nested_descriptor_, &Nested::default_instance());
++ ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
++ Message_descriptor_, &Message::default_instance());
++}
++
++} // namespace
++
++void protobuf_ShutdownFile_protobuf_5ftests_2eproto() {
++ delete Nested::default_instance_;
++ delete Nested_reflection_;
++ delete Message::default_instance_;
++ delete Message_reflection_;
++}
++
++void protobuf_AddDesc_protobuf_5ftests_2eproto() {
++ static bool already_here = false;
++ if (already_here) return;
++ already_here = true;
++ GOOGLE_PROTOBUF_VERIFY_VERSION;
++
++ ::google::protobuf::DescriptorPool::InternalAddGeneratedFile(
++ "\n\024protobuf_tests.proto\022\005tests\"\025\n\006Nested\022"
++ "\013\n\003str\030\001 \001(\t\"\241\004\n\007Message\022\013\n\003str\030\001 \002(\t\022\r\n"
++ "\005bytes\030\002 \002(\014\022\r\n\005int32\030\003 \001(\005\022\r\n\005int64\030\004 \001"
++ "(\003\022\016\n\006uint32\030\005 \001(\r\022\016\n\006uint64\030\006 \001(\004\022\016\n\006si"
++ "nt32\030\007 \001(\021\022\016\n\006sint64\030\010 \001(\022\022\t\n\001f\030\t \002(\002\022\t\n"
++ "\001d\030\n \002(\001\022\026\n\001e\030\013 \002(\0162\013.tests.Enum\022\035\n\006nest"
++ "ed\030\014 \002(\0132\r.tests.Nested\022\027\n\017repeated_stri"
++ "ng\030\r \003(\t\022\026\n\016repeated_bytes\030\016 \003(\014\022\026\n\016repe"
++ "ated_int32\030\017 \003(\005\022\026\n\016repeated_int64\030\020 \003(\003"
++ "\022\027\n\017repeated_uint32\030\021 \003(\r\022\027\n\017repeated_ui"
++ "nt64\030\022 \003(\004\022\027\n\017repeated_sint32\030\023 \003(\021\022\027\n\017r"
++ "epeated_sint64\030\024 \003(\022\022\026\n\016repeated_float\030\025"
++ " \003(\002\022\027\n\017repeated_double\030\026 \003(\001\022\"\n\rrepeate"
++ "d_enum\030\027 \003(\0162\013.tests.Enum\022&\n\017repeated_ne"
++ "sted\030\030 \003(\0132\r.tests.Nested\022\r\n\005empty\030\031 \003(\t"
++ "*\030\n\004Enum\022\007\n\003ONE\020\001\022\007\n\003TWO\020\002", 626);
++ ::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
++ "protobuf_tests.proto", &protobuf_RegisterTypes);
++ Nested::default_instance_ = new Nested();
++ Message::default_instance_ = new Message();
++ Nested::default_instance_->InitAsDefaultInstance();
++ Message::default_instance_->InitAsDefaultInstance();
++ ::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_protobuf_5ftests_2eproto);
++}
++
++// Force AddDescriptors() to be called at static initialization time.
++struct StaticDescriptorInitializer_protobuf_5ftests_2eproto {
++ StaticDescriptorInitializer_protobuf_5ftests_2eproto() {
++ protobuf_AddDesc_protobuf_5ftests_2eproto();
++ }
++} static_descriptor_initializer_protobuf_5ftests_2eproto_;
++
++const ::google::protobuf::EnumDescriptor* Enum_descriptor() {
++ protobuf_AssignDescriptorsOnce();
++ return Enum_descriptor_;
++}
++bool Enum_IsValid(int value) {
++ switch(value) {
++ case 1:
++ case 2:
++ return true;
++ default:
++ return false;
++ }
++}
++
++
++// ===================================================================
++
++#ifndef _MSC_VER
++const int Nested::kStrFieldNumber;
++#endif // !_MSC_VER
++
++Nested::Nested()
++ : ::google::protobuf::Message() {
++ SharedCtor();
++}
++
++void Nested::InitAsDefaultInstance() {
++}
++
++Nested::Nested(const Nested& from)
++ : ::google::protobuf::Message() {
++ SharedCtor();
++ MergeFrom(from);
++}
++
++void Nested::SharedCtor() {
++ _cached_size_ = 0;
++ str_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString);
++ ::memset(_has_bits_, 0, sizeof(_has_bits_));
++}
++
++Nested::~Nested() {
++ SharedDtor();
++}
++
++void Nested::SharedDtor() {
++ if (str_ != &::google::protobuf::internal::kEmptyString) {
++ delete str_;
++ }
++ if (this != default_instance_) {
++ }
++}
++
++void Nested::SetCachedSize(int size) const {
++ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
++ _cached_size_ = size;
++ GOOGLE_SAFE_CONCURRENT_WRITES_END();
++}
++const ::google::protobuf::Descriptor* Nested::descriptor() {
++ protobuf_AssignDescriptorsOnce();
++ return Nested_descriptor_;
++}
++
++const Nested& Nested::default_instance() {
++ if (default_instance_ == NULL) protobuf_AddDesc_protobuf_5ftests_2eproto(); return *default_instance_;
++}
++
++Nested* Nested::default_instance_ = NULL;
++
++Nested* Nested::New() const {
++ return new Nested;
++}
++
++void Nested::Clear() {
++ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
++ if (has_str()) {
++ if (str_ != &::google::protobuf::internal::kEmptyString) {
++ str_->clear();
++ }
++ }
++ }
++ ::memset(_has_bits_, 0, sizeof(_has_bits_));
++ mutable_unknown_fields()->Clear();
++}
++
++bool Nested::MergePartialFromCodedStream(
++ ::google::protobuf::io::CodedInputStream* input) {
++#define DO_(EXPRESSION) if (!(EXPRESSION)) return false
++ ::google::protobuf::uint32 tag;
++ while ((tag = input->ReadTag()) != 0) {
++ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
++ // optional string str = 1;
++ case 1: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
++ DO_(::google::protobuf::internal::WireFormatLite::ReadString(
++ input, this->mutable_str()));
++ ::google::protobuf::internal::WireFormat::VerifyUTF8String(
++ this->str().data(), this->str().length(),
++ ::google::protobuf::internal::WireFormat::PARSE);
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectAtEnd()) return true;
++ break;
++ }
++
++ default: {
++ handle_uninterpreted:
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
++ return true;
++ }
++ DO_(::google::protobuf::internal::WireFormat::SkipField(
++ input, tag, mutable_unknown_fields()));
++ break;
++ }
++ }
++ }
++ return true;
++#undef DO_
++}
++
++void Nested::SerializeWithCachedSizes(
++ ::google::protobuf::io::CodedOutputStream* output) const {
++ // optional string str = 1;
++ if (has_str()) {
++ ::google::protobuf::internal::WireFormat::VerifyUTF8String(
++ this->str().data(), this->str().length(),
++ ::google::protobuf::internal::WireFormat::SERIALIZE);
++ ::google::protobuf::internal::WireFormatLite::WriteString(
++ 1, this->str(), output);
++ }
++
++ if (!unknown_fields().empty()) {
++ ::google::protobuf::internal::WireFormat::SerializeUnknownFields(
++ unknown_fields(), output);
++ }
++}
++
++::google::protobuf::uint8* Nested::SerializeWithCachedSizesToArray(
++ ::google::protobuf::uint8* target) const {
++ // optional string str = 1;
++ if (has_str()) {
++ ::google::protobuf::internal::WireFormat::VerifyUTF8String(
++ this->str().data(), this->str().length(),
++ ::google::protobuf::internal::WireFormat::SERIALIZE);
++ target =
++ ::google::protobuf::internal::WireFormatLite::WriteStringToArray(
++ 1, this->str(), target);
++ }
++
++ if (!unknown_fields().empty()) {
++ target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray(
++ unknown_fields(), target);
++ }
++ return target;
++}
++
++int Nested::ByteSize() const {
++ int total_size = 0;
++
++ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
++ // optional string str = 1;
++ if (has_str()) {
++ total_size += 1 +
++ ::google::protobuf::internal::WireFormatLite::StringSize(
++ this->str());
++ }
++
++ }
++ if (!unknown_fields().empty()) {
++ total_size +=
++ ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(
++ unknown_fields());
++ }
++ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
++ _cached_size_ = total_size;
++ GOOGLE_SAFE_CONCURRENT_WRITES_END();
++ return total_size;
++}
++
++void Nested::MergeFrom(const ::google::protobuf::Message& from) {
++ GOOGLE_CHECK_NE(&from, this);
++ const Nested* source =
++ ::google::protobuf::internal::dynamic_cast_if_available<const Nested*>(
++ &from);
++ if (source == NULL) {
++ ::google::protobuf::internal::ReflectionOps::Merge(from, this);
++ } else {
++ MergeFrom(*source);
++ }
++}
++
++void Nested::MergeFrom(const Nested& from) {
++ GOOGLE_CHECK_NE(&from, this);
++ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
++ if (from.has_str()) {
++ set_str(from.str());
++ }
++ }
++ mutable_unknown_fields()->MergeFrom(from.unknown_fields());
++}
++
++void Nested::CopyFrom(const ::google::protobuf::Message& from) {
++ if (&from == this) return;
++ Clear();
++ MergeFrom(from);
++}
++
++void Nested::CopyFrom(const Nested& from) {
++ if (&from == this) return;
++ Clear();
++ MergeFrom(from);
++}
++
++bool Nested::IsInitialized() const {
++
++ return true;
++}
++
++void Nested::Swap(Nested* other) {
++ if (other != this) {
++ std::swap(str_, other->str_);
++ std::swap(_has_bits_[0], other->_has_bits_[0]);
++ _unknown_fields_.Swap(&other->_unknown_fields_);
++ std::swap(_cached_size_, other->_cached_size_);
++ }
++}
++
++::google::protobuf::Metadata Nested::GetMetadata() const {
++ protobuf_AssignDescriptorsOnce();
++ ::google::protobuf::Metadata metadata;
++ metadata.descriptor = Nested_descriptor_;
++ metadata.reflection = Nested_reflection_;
++ return metadata;
++}
++
++
++// ===================================================================
++
++#ifndef _MSC_VER
++const int Message::kStrFieldNumber;
++const int Message::kBytesFieldNumber;
++const int Message::kInt32FieldNumber;
++const int Message::kInt64FieldNumber;
++const int Message::kUint32FieldNumber;
++const int Message::kUint64FieldNumber;
++const int Message::kSint32FieldNumber;
++const int Message::kSint64FieldNumber;
++const int Message::kFFieldNumber;
++const int Message::kDFieldNumber;
++const int Message::kEFieldNumber;
++const int Message::kNestedFieldNumber;
++const int Message::kRepeatedStringFieldNumber;
++const int Message::kRepeatedBytesFieldNumber;
++const int Message::kRepeatedInt32FieldNumber;
++const int Message::kRepeatedInt64FieldNumber;
++const int Message::kRepeatedUint32FieldNumber;
++const int Message::kRepeatedUint64FieldNumber;
++const int Message::kRepeatedSint32FieldNumber;
++const int Message::kRepeatedSint64FieldNumber;
++const int Message::kRepeatedFloatFieldNumber;
++const int Message::kRepeatedDoubleFieldNumber;
++const int Message::kRepeatedEnumFieldNumber;
++const int Message::kRepeatedNestedFieldNumber;
++const int Message::kEmptyFieldNumber;
++#endif // !_MSC_VER
++
++Message::Message()
++ : ::google::protobuf::Message() {
++ SharedCtor();
++}
++
++void Message::InitAsDefaultInstance() {
++ nested_ = const_cast< ::tests::Nested*>(&::tests::Nested::default_instance());
++}
++
++Message::Message(const Message& from)
++ : ::google::protobuf::Message() {
++ SharedCtor();
++ MergeFrom(from);
++}
++
++void Message::SharedCtor() {
++ _cached_size_ = 0;
++ str_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString);
++ bytes_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString);
++ int32_ = 0;
++ int64_ = GOOGLE_LONGLONG(0);
++ uint32_ = 0u;
++ uint64_ = GOOGLE_ULONGLONG(0);
++ sint32_ = 0;
++ sint64_ = GOOGLE_LONGLONG(0);
++ f_ = 0;
++ d_ = 0;
++ e_ = 1;
++ nested_ = NULL;
++ ::memset(_has_bits_, 0, sizeof(_has_bits_));
++}
++
++Message::~Message() {
++ SharedDtor();
++}
++
++void Message::SharedDtor() {
++ if (str_ != &::google::protobuf::internal::kEmptyString) {
++ delete str_;
++ }
++ if (bytes_ != &::google::protobuf::internal::kEmptyString) {
++ delete bytes_;
++ }
++ if (this != default_instance_) {
++ delete nested_;
++ }
++}
++
++void Message::SetCachedSize(int size) const {
++ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
++ _cached_size_ = size;
++ GOOGLE_SAFE_CONCURRENT_WRITES_END();
++}
++const ::google::protobuf::Descriptor* Message::descriptor() {
++ protobuf_AssignDescriptorsOnce();
++ return Message_descriptor_;
++}
++
++const Message& Message::default_instance() {
++ if (default_instance_ == NULL) protobuf_AddDesc_protobuf_5ftests_2eproto(); return *default_instance_;
++}
++
++Message* Message::default_instance_ = NULL;
++
++Message* Message::New() const {
++ return new Message;
++}
++
++void Message::Clear() {
++ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
++ if (has_str()) {
++ if (str_ != &::google::protobuf::internal::kEmptyString) {
++ str_->clear();
++ }
++ }
++ if (has_bytes()) {
++ if (bytes_ != &::google::protobuf::internal::kEmptyString) {
++ bytes_->clear();
++ }
++ }
++ int32_ = 0;
++ int64_ = GOOGLE_LONGLONG(0);
++ uint32_ = 0u;
++ uint64_ = GOOGLE_ULONGLONG(0);
++ sint32_ = 0;
++ sint64_ = GOOGLE_LONGLONG(0);
++ }
++ if (_has_bits_[8 / 32] & (0xffu << (8 % 32))) {
++ f_ = 0;
++ d_ = 0;
++ e_ = 1;
++ if (has_nested()) {
++ if (nested_ != NULL) nested_->::tests::Nested::Clear();
++ }
++ }
++ repeated_string_.Clear();
++ repeated_bytes_.Clear();
++ repeated_int32_.Clear();
++ repeated_int64_.Clear();
++ repeated_uint32_.Clear();
++ repeated_uint64_.Clear();
++ repeated_sint32_.Clear();
++ repeated_sint64_.Clear();
++ repeated_float_.Clear();
++ repeated_double_.Clear();
++ repeated_enum_.Clear();
++ repeated_nested_.Clear();
++ empty_.Clear();
++ ::memset(_has_bits_, 0, sizeof(_has_bits_));
++ mutable_unknown_fields()->Clear();
++}
++
++bool Message::MergePartialFromCodedStream(
++ ::google::protobuf::io::CodedInputStream* input) {
++#define DO_(EXPRESSION) if (!(EXPRESSION)) return false
++ ::google::protobuf::uint32 tag;
++ while ((tag = input->ReadTag()) != 0) {
++ switch (::google::protobuf::internal::WireFormatLite::GetTagFieldNumber(tag)) {
++ // required string str = 1;
++ case 1: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
++ DO_(::google::protobuf::internal::WireFormatLite::ReadString(
++ input, this->mutable_str()));
++ ::google::protobuf::internal::WireFormat::VerifyUTF8String(
++ this->str().data(), this->str().length(),
++ ::google::protobuf::internal::WireFormat::PARSE);
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(18)) goto parse_bytes;
++ break;
++ }
++
++ // required bytes bytes = 2;
++ case 2: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
++ parse_bytes:
++ DO_(::google::protobuf::internal::WireFormatLite::ReadBytes(
++ input, this->mutable_bytes()));
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(24)) goto parse_int32;
++ break;
++ }
++
++ // optional int32 int32 = 3;
++ case 3: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
++ parse_int32:
++ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
++ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
++ input, &int32_)));
++ set_has_int32();
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(32)) goto parse_int64;
++ break;
++ }
++
++ // optional int64 int64 = 4;
++ case 4: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
++ parse_int64:
++ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
++ ::google::protobuf::int64, ::google::protobuf::internal::WireFormatLite::TYPE_INT64>(
++ input, &int64_)));
++ set_has_int64();
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(40)) goto parse_uint32;
++ break;
++ }
++
++ // optional uint32 uint32 = 5;
++ case 5: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
++ parse_uint32:
++ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
++ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
++ input, &uint32_)));
++ set_has_uint32();
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(48)) goto parse_uint64;
++ break;
++ }
++
++ // optional uint64 uint64 = 6;
++ case 6: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
++ parse_uint64:
++ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
++ ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
++ input, &uint64_)));
++ set_has_uint64();
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(56)) goto parse_sint32;
++ break;
++ }
++
++ // optional sint32 sint32 = 7;
++ case 7: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
++ parse_sint32:
++ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
++ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_SINT32>(
++ input, &sint32_)));
++ set_has_sint32();
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(64)) goto parse_sint64;
++ break;
++ }
++
++ // optional sint64 sint64 = 8;
++ case 8: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
++ parse_sint64:
++ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
++ ::google::protobuf::int64, ::google::protobuf::internal::WireFormatLite::TYPE_SINT64>(
++ input, &sint64_)));
++ set_has_sint64();
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(77)) goto parse_f;
++ break;
++ }
++
++ // required float f = 9;
++ case 9: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_FIXED32) {
++ parse_f:
++ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
++ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
++ input, &f_)));
++ set_has_f();
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(81)) goto parse_d;
++ break;
++ }
++
++ // required double d = 10;
++ case 10: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_FIXED64) {
++ parse_d:
++ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
++ double, ::google::protobuf::internal::WireFormatLite::TYPE_DOUBLE>(
++ input, &d_)));
++ set_has_d();
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(88)) goto parse_e;
++ break;
++ }
++
++ // required .tests.Enum e = 11;
++ case 11: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
++ parse_e:
++ int value;
++ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
++ int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>(
++ input, &value)));
++ if (tests::Enum_IsValid(value)) {
++ set_e(static_cast< tests::Enum >(value));
++ } else {
++ mutable_unknown_fields()->AddVarint(11, value);
++ }
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(98)) goto parse_nested;
++ break;
++ }
++
++ // required .tests.Nested nested = 12;
++ case 12: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
++ parse_nested:
++ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
++ input, mutable_nested()));
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(106)) goto parse_repeated_string;
++ break;
++ }
++
++ // repeated string repeated_string = 13;
++ case 13: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
++ parse_repeated_string:
++ DO_(::google::protobuf::internal::WireFormatLite::ReadString(
++ input, this->add_repeated_string()));
++ ::google::protobuf::internal::WireFormat::VerifyUTF8String(
++ this->repeated_string(0).data(), this->repeated_string(0).length(),
++ ::google::protobuf::internal::WireFormat::PARSE);
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(106)) goto parse_repeated_string;
++ if (input->ExpectTag(114)) goto parse_repeated_bytes;
++ break;
++ }
++
++ // repeated bytes repeated_bytes = 14;
++ case 14: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
++ parse_repeated_bytes:
++ DO_(::google::protobuf::internal::WireFormatLite::ReadBytes(
++ input, this->add_repeated_bytes()));
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(114)) goto parse_repeated_bytes;
++ if (input->ExpectTag(120)) goto parse_repeated_int32;
++ break;
++ }
++
++ // repeated int32 repeated_int32 = 15;
++ case 15: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
++ parse_repeated_int32:
++ DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitive<
++ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
++ 1, 120, input, this->mutable_repeated_int32())));
++ } else if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag)
++ == ::google::protobuf::internal::WireFormatLite::
++ WIRETYPE_LENGTH_DELIMITED) {
++ DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitiveNoInline<
++ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>(
++ input, this->mutable_repeated_int32())));
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(120)) goto parse_repeated_int32;
++ if (input->ExpectTag(128)) goto parse_repeated_int64;
++ break;
++ }
++
++ // repeated int64 repeated_int64 = 16;
++ case 16: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
++ parse_repeated_int64:
++ DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitive<
++ ::google::protobuf::int64, ::google::protobuf::internal::WireFormatLite::TYPE_INT64>(
++ 2, 128, input, this->mutable_repeated_int64())));
++ } else if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag)
++ == ::google::protobuf::internal::WireFormatLite::
++ WIRETYPE_LENGTH_DELIMITED) {
++ DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitiveNoInline<
++ ::google::protobuf::int64, ::google::protobuf::internal::WireFormatLite::TYPE_INT64>(
++ input, this->mutable_repeated_int64())));
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(128)) goto parse_repeated_int64;
++ if (input->ExpectTag(136)) goto parse_repeated_uint32;
++ break;
++ }
++
++ // repeated uint32 repeated_uint32 = 17;
++ case 17: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
++ parse_repeated_uint32:
++ DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitive<
++ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
++ 2, 136, input, this->mutable_repeated_uint32())));
++ } else if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag)
++ == ::google::protobuf::internal::WireFormatLite::
++ WIRETYPE_LENGTH_DELIMITED) {
++ DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitiveNoInline<
++ ::google::protobuf::uint32, ::google::protobuf::internal::WireFormatLite::TYPE_UINT32>(
++ input, this->mutable_repeated_uint32())));
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(136)) goto parse_repeated_uint32;
++ if (input->ExpectTag(144)) goto parse_repeated_uint64;
++ break;
++ }
++
++ // repeated uint64 repeated_uint64 = 18;
++ case 18: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
++ parse_repeated_uint64:
++ DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitive<
++ ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
++ 2, 144, input, this->mutable_repeated_uint64())));
++ } else if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag)
++ == ::google::protobuf::internal::WireFormatLite::
++ WIRETYPE_LENGTH_DELIMITED) {
++ DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitiveNoInline<
++ ::google::protobuf::uint64, ::google::protobuf::internal::WireFormatLite::TYPE_UINT64>(
++ input, this->mutable_repeated_uint64())));
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(144)) goto parse_repeated_uint64;
++ if (input->ExpectTag(152)) goto parse_repeated_sint32;
++ break;
++ }
++
++ // repeated sint32 repeated_sint32 = 19;
++ case 19: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
++ parse_repeated_sint32:
++ DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitive<
++ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_SINT32>(
++ 2, 152, input, this->mutable_repeated_sint32())));
++ } else if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag)
++ == ::google::protobuf::internal::WireFormatLite::
++ WIRETYPE_LENGTH_DELIMITED) {
++ DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitiveNoInline<
++ ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_SINT32>(
++ input, this->mutable_repeated_sint32())));
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(152)) goto parse_repeated_sint32;
++ if (input->ExpectTag(160)) goto parse_repeated_sint64;
++ break;
++ }
++
++ // repeated sint64 repeated_sint64 = 20;
++ case 20: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
++ parse_repeated_sint64:
++ DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitive<
++ ::google::protobuf::int64, ::google::protobuf::internal::WireFormatLite::TYPE_SINT64>(
++ 2, 160, input, this->mutable_repeated_sint64())));
++ } else if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag)
++ == ::google::protobuf::internal::WireFormatLite::
++ WIRETYPE_LENGTH_DELIMITED) {
++ DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitiveNoInline<
++ ::google::protobuf::int64, ::google::protobuf::internal::WireFormatLite::TYPE_SINT64>(
++ input, this->mutable_repeated_sint64())));
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(160)) goto parse_repeated_sint64;
++ if (input->ExpectTag(173)) goto parse_repeated_float;
++ break;
++ }
++
++ // repeated float repeated_float = 21;
++ case 21: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_FIXED32) {
++ parse_repeated_float:
++ DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitive<
++ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
++ 2, 173, input, this->mutable_repeated_float())));
++ } else if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag)
++ == ::google::protobuf::internal::WireFormatLite::
++ WIRETYPE_LENGTH_DELIMITED) {
++ DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitiveNoInline<
++ float, ::google::protobuf::internal::WireFormatLite::TYPE_FLOAT>(
++ input, this->mutable_repeated_float())));
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(173)) goto parse_repeated_float;
++ if (input->ExpectTag(177)) goto parse_repeated_double;
++ break;
++ }
++
++ // repeated double repeated_double = 22;
++ case 22: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_FIXED64) {
++ parse_repeated_double:
++ DO_((::google::protobuf::internal::WireFormatLite::ReadRepeatedPrimitive<
++ double, ::google::protobuf::internal::WireFormatLite::TYPE_DOUBLE>(
++ 2, 177, input, this->mutable_repeated_double())));
++ } else if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag)
++ == ::google::protobuf::internal::WireFormatLite::
++ WIRETYPE_LENGTH_DELIMITED) {
++ DO_((::google::protobuf::internal::WireFormatLite::ReadPackedPrimitiveNoInline<
++ double, ::google::protobuf::internal::WireFormatLite::TYPE_DOUBLE>(
++ input, this->mutable_repeated_double())));
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(177)) goto parse_repeated_double;
++ if (input->ExpectTag(184)) goto parse_repeated_enum;
++ break;
++ }
++
++ // repeated .tests.Enum repeated_enum = 23;
++ case 23: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) {
++ parse_repeated_enum:
++ int value;
++ DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive<
++ int, ::google::protobuf::internal::WireFormatLite::TYPE_ENUM>(
++ input, &value)));
++ if (tests::Enum_IsValid(value)) {
++ add_repeated_enum(static_cast< tests::Enum >(value));
++ } else {
++ mutable_unknown_fields()->AddVarint(23, value);
++ }
++ } else if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag)
++ == ::google::protobuf::internal::WireFormatLite::
++ WIRETYPE_LENGTH_DELIMITED) {
++ DO_((::google::protobuf::internal::WireFormatLite::ReadPackedEnumNoInline(
++ input,
++ &tests::Enum_IsValid,
++ this->mutable_repeated_enum())));
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(184)) goto parse_repeated_enum;
++ if (input->ExpectTag(194)) goto parse_repeated_nested;
++ break;
++ }
++
++ // repeated .tests.Nested repeated_nested = 24;
++ case 24: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
++ parse_repeated_nested:
++ DO_(::google::protobuf::internal::WireFormatLite::ReadMessageNoVirtual(
++ input, add_repeated_nested()));
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(194)) goto parse_repeated_nested;
++ if (input->ExpectTag(202)) goto parse_empty;
++ break;
++ }
++
++ // repeated string empty = 25;
++ case 25: {
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_LENGTH_DELIMITED) {
++ parse_empty:
++ DO_(::google::protobuf::internal::WireFormatLite::ReadString(
++ input, this->add_empty()));
++ ::google::protobuf::internal::WireFormat::VerifyUTF8String(
++ this->empty(0).data(), this->empty(0).length(),
++ ::google::protobuf::internal::WireFormat::PARSE);
++ } else {
++ goto handle_uninterpreted;
++ }
++ if (input->ExpectTag(202)) goto parse_empty;
++ if (input->ExpectAtEnd()) return true;
++ break;
++ }
++
++ default: {
++ handle_uninterpreted:
++ if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) ==
++ ::google::protobuf::internal::WireFormatLite::WIRETYPE_END_GROUP) {
++ return true;
++ }
++ DO_(::google::protobuf::internal::WireFormat::SkipField(
++ input, tag, mutable_unknown_fields()));
++ break;
++ }
++ }
++ }
++ return true;
++#undef DO_
++}
++
++void Message::SerializeWithCachedSizes(
++ ::google::protobuf::io::CodedOutputStream* output) const {
++ // required string str = 1;
++ if (has_str()) {
++ ::google::protobuf::internal::WireFormat::VerifyUTF8String(
++ this->str().data(), this->str().length(),
++ ::google::protobuf::internal::WireFormat::SERIALIZE);
++ ::google::protobuf::internal::WireFormatLite::WriteString(
++ 1, this->str(), output);
++ }
++
++ // required bytes bytes = 2;
++ if (has_bytes()) {
++ ::google::protobuf::internal::WireFormatLite::WriteBytes(
++ 2, this->bytes(), output);
++ }
++
++ // optional int32 int32 = 3;
++ if (has_int32()) {
++ ::google::protobuf::internal::WireFormatLite::WriteInt32(3, this->int32(), output);
++ }
++
++ // optional int64 int64 = 4;
++ if (has_int64()) {
++ ::google::protobuf::internal::WireFormatLite::WriteInt64(4, this->int64(), output);
++ }
++
++ // optional uint32 uint32 = 5;
++ if (has_uint32()) {
++ ::google::protobuf::internal::WireFormatLite::WriteUInt32(5, this->uint32(), output);
++ }
++
++ // optional uint64 uint64 = 6;
++ if (has_uint64()) {
++ ::google::protobuf::internal::WireFormatLite::WriteUInt64(6, this->uint64(), output);
++ }
++
++ // optional sint32 sint32 = 7;
++ if (has_sint32()) {
++ ::google::protobuf::internal::WireFormatLite::WriteSInt32(7, this->sint32(), output);
++ }
++
++ // optional sint64 sint64 = 8;
++ if (has_sint64()) {
++ ::google::protobuf::internal::WireFormatLite::WriteSInt64(8, this->sint64(), output);
++ }
++
++ // required float f = 9;
++ if (has_f()) {
++ ::google::protobuf::internal::WireFormatLite::WriteFloat(9, this->f(), output);
++ }
++
++ // required double d = 10;
++ if (has_d()) {
++ ::google::protobuf::internal::WireFormatLite::WriteDouble(10, this->d(), output);
++ }
++
++ // required .tests.Enum e = 11;
++ if (has_e()) {
++ ::google::protobuf::internal::WireFormatLite::WriteEnum(
++ 11, this->e(), output);
++ }
++
++ // required .tests.Nested nested = 12;
++ if (has_nested()) {
++ ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray(
++ 12, this->nested(), output);
++ }
++
++ // repeated string repeated_string = 13;
++ for (int i = 0; i < this->repeated_string_size(); i++) {
++ ::google::protobuf::internal::WireFormat::VerifyUTF8String(
++ this->repeated_string(i).data(), this->repeated_string(i).length(),
++ ::google::protobuf::internal::WireFormat::SERIALIZE);
++ ::google::protobuf::internal::WireFormatLite::WriteString(
++ 13, this->repeated_string(i), output);
++ }
++
++ // repeated bytes repeated_bytes = 14;
++ for (int i = 0; i < this->repeated_bytes_size(); i++) {
++ ::google::protobuf::internal::WireFormatLite::WriteBytes(
++ 14, this->repeated_bytes(i), output);
++ }
++
++ // repeated int32 repeated_int32 = 15;
++ for (int i = 0; i < this->repeated_int32_size(); i++) {
++ ::google::protobuf::internal::WireFormatLite::WriteInt32(
++ 15, this->repeated_int32(i), output);
++ }
++
++ // repeated int64 repeated_int64 = 16;
++ for (int i = 0; i < this->repeated_int64_size(); i++) {
++ ::google::protobuf::internal::WireFormatLite::WriteInt64(
++ 16, this->repeated_int64(i), output);
++ }
++
++ // repeated uint32 repeated_uint32 = 17;
++ for (int i = 0; i < this->repeated_uint32_size(); i++) {
++ ::google::protobuf::internal::WireFormatLite::WriteUInt32(
++ 17, this->repeated_uint32(i), output);
++ }
++
++ // repeated uint64 repeated_uint64 = 18;
++ for (int i = 0; i < this->repeated_uint64_size(); i++) {
++ ::google::protobuf::internal::WireFormatLite::WriteUInt64(
++ 18, this->repeated_uint64(i), output);
++ }
++
++ // repeated sint32 repeated_sint32 = 19;
++ for (int i = 0; i < this->repeated_sint32_size(); i++) {
++ ::google::protobuf::internal::WireFormatLite::WriteSInt32(
++ 19, this->repeated_sint32(i), output);
++ }
++
++ // repeated sint64 repeated_sint64 = 20;
++ for (int i = 0; i < this->repeated_sint64_size(); i++) {
++ ::google::protobuf::internal::WireFormatLite::WriteSInt64(
++ 20, this->repeated_sint64(i), output);
++ }
++
++ // repeated float repeated_float = 21;
++ for (int i = 0; i < this->repeated_float_size(); i++) {
++ ::google::protobuf::internal::WireFormatLite::WriteFloat(
++ 21, this->repeated_float(i), output);
++ }
++
++ // repeated double repeated_double = 22;
++ for (int i = 0; i < this->repeated_double_size(); i++) {
++ ::google::protobuf::internal::WireFormatLite::WriteDouble(
++ 22, this->repeated_double(i), output);
++ }
++
++ // repeated .tests.Enum repeated_enum = 23;
++ for (int i = 0; i < this->repeated_enum_size(); i++) {
++ ::google::protobuf::internal::WireFormatLite::WriteEnum(
++ 23, this->repeated_enum(i), output);
++ }
++
++ // repeated .tests.Nested repeated_nested = 24;
++ for (int i = 0; i < this->repeated_nested_size(); i++) {
++ ::google::protobuf::internal::WireFormatLite::WriteMessageMaybeToArray(
++ 24, this->repeated_nested(i), output);
++ }
++
++ // repeated string empty = 25;
++ for (int i = 0; i < this->empty_size(); i++) {
++ ::google::protobuf::internal::WireFormat::VerifyUTF8String(
++ this->empty(i).data(), this->empty(i).length(),
++ ::google::protobuf::internal::WireFormat::SERIALIZE);
++ ::google::protobuf::internal::WireFormatLite::WriteString(
++ 25, this->empty(i), output);
++ }
++
++ if (!unknown_fields().empty()) {
++ ::google::protobuf::internal::WireFormat::SerializeUnknownFields(
++ unknown_fields(), output);
++ }
++}
++
++::google::protobuf::uint8* Message::SerializeWithCachedSizesToArray(
++ ::google::protobuf::uint8* target) const {
++ // required string str = 1;
++ if (has_str()) {
++ ::google::protobuf::internal::WireFormat::VerifyUTF8String(
++ this->str().data(), this->str().length(),
++ ::google::protobuf::internal::WireFormat::SERIALIZE);
++ target =
++ ::google::protobuf::internal::WireFormatLite::WriteStringToArray(
++ 1, this->str(), target);
++ }
++
++ // required bytes bytes = 2;
++ if (has_bytes()) {
++ target =
++ ::google::protobuf::internal::WireFormatLite::WriteBytesToArray(
++ 2, this->bytes(), target);
++ }
++
++ // optional int32 int32 = 3;
++ if (has_int32()) {
++ target = ::google::protobuf::internal::WireFormatLite::WriteInt32ToArray(3, this->int32(), target);
++ }
++
++ // optional int64 int64 = 4;
++ if (has_int64()) {
++ target = ::google::protobuf::internal::WireFormatLite::WriteInt64ToArray(4, this->int64(), target);
++ }
++
++ // optional uint32 uint32 = 5;
++ if (has_uint32()) {
++ target = ::google::protobuf::internal::WireFormatLite::WriteUInt32ToArray(5, this->uint32(), target);
++ }
++
++ // optional uint64 uint64 = 6;
++ if (has_uint64()) {
++ target = ::google::protobuf::internal::WireFormatLite::WriteUInt64ToArray(6, this->uint64(), target);
++ }
++
++ // optional sint32 sint32 = 7;
++ if (has_sint32()) {
++ target = ::google::protobuf::internal::WireFormatLite::WriteSInt32ToArray(7, this->sint32(), target);
++ }
++
++ // optional sint64 sint64 = 8;
++ if (has_sint64()) {
++ target = ::google::protobuf::internal::WireFormatLite::WriteSInt64ToArray(8, this->sint64(), target);
++ }
++
++ // required float f = 9;
++ if (has_f()) {
++ target = ::google::protobuf::internal::WireFormatLite::WriteFloatToArray(9, this->f(), target);
++ }
++
++ // required double d = 10;
++ if (has_d()) {
++ target = ::google::protobuf::internal::WireFormatLite::WriteDoubleToArray(10, this->d(), target);
++ }
++
++ // required .tests.Enum e = 11;
++ if (has_e()) {
++ target = ::google::protobuf::internal::WireFormatLite::WriteEnumToArray(
++ 11, this->e(), target);
++ }
++
++ // required .tests.Nested nested = 12;
++ if (has_nested()) {
++ target = ::google::protobuf::internal::WireFormatLite::
++ WriteMessageNoVirtualToArray(
++ 12, this->nested(), target);
++ }
++
++ // repeated string repeated_string = 13;
++ for (int i = 0; i < this->repeated_string_size(); i++) {
++ ::google::protobuf::internal::WireFormat::VerifyUTF8String(
++ this->repeated_string(i).data(), this->repeated_string(i).length(),
++ ::google::protobuf::internal::WireFormat::SERIALIZE);
++ target = ::google::protobuf::internal::WireFormatLite::
++ WriteStringToArray(13, this->repeated_string(i), target);
++ }
++
++ // repeated bytes repeated_bytes = 14;
++ for (int i = 0; i < this->repeated_bytes_size(); i++) {
++ target = ::google::protobuf::internal::WireFormatLite::
++ WriteBytesToArray(14, this->repeated_bytes(i), target);
++ }
++
++ // repeated int32 repeated_int32 = 15;
++ for (int i = 0; i < this->repeated_int32_size(); i++) {
++ target = ::google::protobuf::internal::WireFormatLite::
++ WriteInt32ToArray(15, this->repeated_int32(i), target);
++ }
++
++ // repeated int64 repeated_int64 = 16;
++ for (int i = 0; i < this->repeated_int64_size(); i++) {
++ target = ::google::protobuf::internal::WireFormatLite::
++ WriteInt64ToArray(16, this->repeated_int64(i), target);
++ }
++
++ // repeated uint32 repeated_uint32 = 17;
++ for (int i = 0; i < this->repeated_uint32_size(); i++) {
++ target = ::google::protobuf::internal::WireFormatLite::
++ WriteUInt32ToArray(17, this->repeated_uint32(i), target);
++ }
++
++ // repeated uint64 repeated_uint64 = 18;
++ for (int i = 0; i < this->repeated_uint64_size(); i++) {
++ target = ::google::protobuf::internal::WireFormatLite::
++ WriteUInt64ToArray(18, this->repeated_uint64(i), target);
++ }
++
++ // repeated sint32 repeated_sint32 = 19;
++ for (int i = 0; i < this->repeated_sint32_size(); i++) {
++ target = ::google::protobuf::internal::WireFormatLite::
++ WriteSInt32ToArray(19, this->repeated_sint32(i), target);
++ }
++
++ // repeated sint64 repeated_sint64 = 20;
++ for (int i = 0; i < this->repeated_sint64_size(); i++) {
++ target = ::google::protobuf::internal::WireFormatLite::
++ WriteSInt64ToArray(20, this->repeated_sint64(i), target);
++ }
++
++ // repeated float repeated_float = 21;
++ for (int i = 0; i < this->repeated_float_size(); i++) {
++ target = ::google::protobuf::internal::WireFormatLite::
++ WriteFloatToArray(21, this->repeated_float(i), target);
++ }
++
++ // repeated double repeated_double = 22;
++ for (int i = 0; i < this->repeated_double_size(); i++) {
++ target = ::google::protobuf::internal::WireFormatLite::
++ WriteDoubleToArray(22, this->repeated_double(i), target);
++ }
++
++ // repeated .tests.Enum repeated_enum = 23;
++ for (int i = 0; i < this->repeated_enum_size(); i++) {
++ target = ::google::protobuf::internal::WireFormatLite::WriteEnumToArray(
++ 23, this->repeated_enum(i), target);
++ }
++
++ // repeated .tests.Nested repeated_nested = 24;
++ for (int i = 0; i < this->repeated_nested_size(); i++) {
++ target = ::google::protobuf::internal::WireFormatLite::
++ WriteMessageNoVirtualToArray(
++ 24, this->repeated_nested(i), target);
++ }
++
++ // repeated string empty = 25;
++ for (int i = 0; i < this->empty_size(); i++) {
++ ::google::protobuf::internal::WireFormat::VerifyUTF8String(
++ this->empty(i).data(), this->empty(i).length(),
++ ::google::protobuf::internal::WireFormat::SERIALIZE);
++ target = ::google::protobuf::internal::WireFormatLite::
++ WriteStringToArray(25, this->empty(i), target);
++ }
++
++ if (!unknown_fields().empty()) {
++ target = ::google::protobuf::internal::WireFormat::SerializeUnknownFieldsToArray(
++ unknown_fields(), target);
++ }
++ return target;
++}
++
++int Message::ByteSize() const {
++ int total_size = 0;
++
++ if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) {
++ // required string str = 1;
++ if (has_str()) {
++ total_size += 1 +
++ ::google::protobuf::internal::WireFormatLite::StringSize(
++ this->str());
++ }
++
++ // required bytes bytes = 2;
++ if (has_bytes()) {
++ total_size += 1 +
++ ::google::protobuf::internal::WireFormatLite::BytesSize(
++ this->bytes());
++ }
++
++ // optional int32 int32 = 3;
++ if (has_int32()) {
++ total_size += 1 +
++ ::google::protobuf::internal::WireFormatLite::Int32Size(
++ this->int32());
++ }
++
++ // optional int64 int64 = 4;
++ if (has_int64()) {
++ total_size += 1 +
++ ::google::protobuf::internal::WireFormatLite::Int64Size(
++ this->int64());
++ }
++
++ // optional uint32 uint32 = 5;
++ if (has_uint32()) {
++ total_size += 1 +
++ ::google::protobuf::internal::WireFormatLite::UInt32Size(
++ this->uint32());
++ }
++
++ // optional uint64 uint64 = 6;
++ if (has_uint64()) {
++ total_size += 1 +
++ ::google::protobuf::internal::WireFormatLite::UInt64Size(
++ this->uint64());
++ }
++
++ // optional sint32 sint32 = 7;
++ if (has_sint32()) {
++ total_size += 1 +
++ ::google::protobuf::internal::WireFormatLite::SInt32Size(
++ this->sint32());
++ }
++
++ // optional sint64 sint64 = 8;
++ if (has_sint64()) {
++ total_size += 1 +
++ ::google::protobuf::internal::WireFormatLite::SInt64Size(
++ this->sint64());
++ }
++
++ }
++ if (_has_bits_[8 / 32] & (0xffu << (8 % 32))) {
++ // required float f = 9;
++ if (has_f()) {
++ total_size += 1 + 4;
++ }
++
++ // required double d = 10;
++ if (has_d()) {
++ total_size += 1 + 8;
++ }
++
++ // required .tests.Enum e = 11;
++ if (has_e()) {
++ total_size += 1 +
++ ::google::protobuf::internal::WireFormatLite::EnumSize(this->e());
++ }
++
++ // required .tests.Nested nested = 12;
++ if (has_nested()) {
++ total_size += 1 +
++ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
++ this->nested());
++ }
++
++ }
++ // repeated string repeated_string = 13;
++ total_size += 1 * this->repeated_string_size();
++ for (int i = 0; i < this->repeated_string_size(); i++) {
++ total_size += ::google::protobuf::internal::WireFormatLite::StringSize(
++ this->repeated_string(i));
++ }
++
++ // repeated bytes repeated_bytes = 14;
++ total_size += 1 * this->repeated_bytes_size();
++ for (int i = 0; i < this->repeated_bytes_size(); i++) {
++ total_size += ::google::protobuf::internal::WireFormatLite::BytesSize(
++ this->repeated_bytes(i));
++ }
++
++ // repeated int32 repeated_int32 = 15;
++ {
++ int data_size = 0;
++ for (int i = 0; i < this->repeated_int32_size(); i++) {
++ data_size += ::google::protobuf::internal::WireFormatLite::
++ Int32Size(this->repeated_int32(i));
++ }
++ total_size += 1 * this->repeated_int32_size() + data_size;
++ }
++
++ // repeated int64 repeated_int64 = 16;
++ {
++ int data_size = 0;
++ for (int i = 0; i < this->repeated_int64_size(); i++) {
++ data_size += ::google::protobuf::internal::WireFormatLite::
++ Int64Size(this->repeated_int64(i));
++ }
++ total_size += 2 * this->repeated_int64_size() + data_size;
++ }
++
++ // repeated uint32 repeated_uint32 = 17;
++ {
++ int data_size = 0;
++ for (int i = 0; i < this->repeated_uint32_size(); i++) {
++ data_size += ::google::protobuf::internal::WireFormatLite::
++ UInt32Size(this->repeated_uint32(i));
++ }
++ total_size += 2 * this->repeated_uint32_size() + data_size;
++ }
++
++ // repeated uint64 repeated_uint64 = 18;
++ {
++ int data_size = 0;
++ for (int i = 0; i < this->repeated_uint64_size(); i++) {
++ data_size += ::google::protobuf::internal::WireFormatLite::
++ UInt64Size(this->repeated_uint64(i));
++ }
++ total_size += 2 * this->repeated_uint64_size() + data_size;
++ }
++
++ // repeated sint32 repeated_sint32 = 19;
++ {
++ int data_size = 0;
++ for (int i = 0; i < this->repeated_sint32_size(); i++) {
++ data_size += ::google::protobuf::internal::WireFormatLite::
++ SInt32Size(this->repeated_sint32(i));
++ }
++ total_size += 2 * this->repeated_sint32_size() + data_size;
++ }
++
++ // repeated sint64 repeated_sint64 = 20;
++ {
++ int data_size = 0;
++ for (int i = 0; i < this->repeated_sint64_size(); i++) {
++ data_size += ::google::protobuf::internal::WireFormatLite::
++ SInt64Size(this->repeated_sint64(i));
++ }
++ total_size += 2 * this->repeated_sint64_size() + data_size;
++ }
++
++ // repeated float repeated_float = 21;
++ {
++ int data_size = 0;
++ data_size = 4 * this->repeated_float_size();
++ total_size += 2 * this->repeated_float_size() + data_size;
++ }
++
++ // repeated double repeated_double = 22;
++ {
++ int data_size = 0;
++ data_size = 8 * this->repeated_double_size();
++ total_size += 2 * this->repeated_double_size() + data_size;
++ }
++
++ // repeated .tests.Enum repeated_enum = 23;
++ {
++ int data_size = 0;
++ for (int i = 0; i < this->repeated_enum_size(); i++) {
++ data_size += ::google::protobuf::internal::WireFormatLite::EnumSize(
++ this->repeated_enum(i));
++ }
++ total_size += 2 * this->repeated_enum_size() + data_size;
++ }
++
++ // repeated .tests.Nested repeated_nested = 24;
++ total_size += 2 * this->repeated_nested_size();
++ for (int i = 0; i < this->repeated_nested_size(); i++) {
++ total_size +=
++ ::google::protobuf::internal::WireFormatLite::MessageSizeNoVirtual(
++ this->repeated_nested(i));
++ }
++
++ // repeated string empty = 25;
++ total_size += 2 * this->empty_size();
++ for (int i = 0; i < this->empty_size(); i++) {
++ total_size += ::google::protobuf::internal::WireFormatLite::StringSize(
++ this->empty(i));
++ }
++
++ if (!unknown_fields().empty()) {
++ total_size +=
++ ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize(
++ unknown_fields());
++ }
++ GOOGLE_SAFE_CONCURRENT_WRITES_BEGIN();
++ _cached_size_ = total_size;
++ GOOGLE_SAFE_CONCURRENT_WRITES_END();
++ return total_size;
++}
++
++void Message::MergeFrom(const ::google::protobuf::Message& from) {
++ GOOGLE_CHECK_NE(&from, this);
++ const Message* source =
++ ::google::protobuf::internal::dynamic_cast_if_available<const Message*>(
++ &from);
++ if (source == NULL) {
++ ::google::protobuf::internal::ReflectionOps::Merge(from, this);
++ } else {
++ MergeFrom(*source);
++ }
++}
++
++void Message::MergeFrom(const Message& from) {
++ GOOGLE_CHECK_NE(&from, this);
++ repeated_string_.MergeFrom(from.repeated_string_);
++ repeated_bytes_.MergeFrom(from.repeated_bytes_);
++ repeated_int32_.MergeFrom(from.repeated_int32_);
++ repeated_int64_.MergeFrom(from.repeated_int64_);
++ repeated_uint32_.MergeFrom(from.repeated_uint32_);
++ repeated_uint64_.MergeFrom(from.repeated_uint64_);
++ repeated_sint32_.MergeFrom(from.repeated_sint32_);
++ repeated_sint64_.MergeFrom(from.repeated_sint64_);
++ repeated_float_.MergeFrom(from.repeated_float_);
++ repeated_double_.MergeFrom(from.repeated_double_);
++ repeated_enum_.MergeFrom(from.repeated_enum_);
++ repeated_nested_.MergeFrom(from.repeated_nested_);
++ empty_.MergeFrom(from.empty_);
++ if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) {
++ if (from.has_str()) {
++ set_str(from.str());
++ }
++ if (from.has_bytes()) {
++ set_bytes(from.bytes());
++ }
++ if (from.has_int32()) {
++ set_int32(from.int32());
++ }
++ if (from.has_int64()) {
++ set_int64(from.int64());
++ }
++ if (from.has_uint32()) {
++ set_uint32(from.uint32());
++ }
++ if (from.has_uint64()) {
++ set_uint64(from.uint64());
++ }
++ if (from.has_sint32()) {
++ set_sint32(from.sint32());
++ }
++ if (from.has_sint64()) {
++ set_sint64(from.sint64());
++ }
++ }
++ if (from._has_bits_[8 / 32] & (0xffu << (8 % 32))) {
++ if (from.has_f()) {
++ set_f(from.f());
++ }
++ if (from.has_d()) {
++ set_d(from.d());
++ }
++ if (from.has_e()) {
++ set_e(from.e());
++ }
++ if (from.has_nested()) {
++ mutable_nested()->::tests::Nested::MergeFrom(from.nested());
++ }
++ }
++ mutable_unknown_fields()->MergeFrom(from.unknown_fields());
++}
++
++void Message::CopyFrom(const ::google::protobuf::Message& from) {
++ if (&from == this) return;
++ Clear();
++ MergeFrom(from);
++}
++
++void Message::CopyFrom(const Message& from) {
++ if (&from == this) return;
++ Clear();
++ MergeFrom(from);
++}
++
++bool Message::IsInitialized() const {
++ if ((_has_bits_[0] & 0x00000f03) != 0x00000f03) return false;
++
++ return true;
++}
++
++void Message::Swap(Message* other) {
++ if (other != this) {
++ std::swap(str_, other->str_);
++ std::swap(bytes_, other->bytes_);
++ std::swap(int32_, other->int32_);
++ std::swap(int64_, other->int64_);
++ std::swap(uint32_, other->uint32_);
++ std::swap(uint64_, other->uint64_);
++ std::swap(sint32_, other->sint32_);
++ std::swap(sint64_, other->sint64_);
++ std::swap(f_, other->f_);
++ std::swap(d_, other->d_);
++ std::swap(e_, other->e_);
++ std::swap(nested_, other->nested_);
++ repeated_string_.Swap(&other->repeated_string_);
++ repeated_bytes_.Swap(&other->repeated_bytes_);
++ repeated_int32_.Swap(&other->repeated_int32_);
++ repeated_int64_.Swap(&other->repeated_int64_);
++ repeated_uint32_.Swap(&other->repeated_uint32_);
++ repeated_uint64_.Swap(&other->repeated_uint64_);
++ repeated_sint32_.Swap(&other->repeated_sint32_);
++ repeated_sint64_.Swap(&other->repeated_sint64_);
++ repeated_float_.Swap(&other->repeated_float_);
++ repeated_double_.Swap(&other->repeated_double_);
++ repeated_enum_.Swap(&other->repeated_enum_);
++ repeated_nested_.Swap(&other->repeated_nested_);
++ empty_.Swap(&other->empty_);
++ std::swap(_has_bits_[0], other->_has_bits_[0]);
++ _unknown_fields_.Swap(&other->_unknown_fields_);
++ std::swap(_cached_size_, other->_cached_size_);
++ }
++}
++
++::google::protobuf::Metadata Message::GetMetadata() const {
++ protobuf_AssignDescriptorsOnce();
++ ::google::protobuf::Metadata metadata;
++ metadata.descriptor = Message_descriptor_;
++ metadata.reflection = Message_reflection_;
++ return metadata;
++}
++
++
++// @@protoc_insertion_point(namespace_scope)
++
++} // namespace tests
++
++// @@protoc_insertion_point(global_scope)
+diff --git a/src/tests/stout/protobuf_tests.pb.h b/src/tests/stout/protobuf_tests.pb.h
+new file mode 100644
+index 0000000..aef5b29
+--- /dev/null
++++ b/src/tests/stout/protobuf_tests.pb.h
+@@ -0,0 +1,1340 @@
++// Generated by the protocol buffer compiler. DO NOT EDIT!
++// source: protobuf_tests.proto
++
++#ifndef PROTOBUF_protobuf_5ftests_2eproto__INCLUDED
++#define PROTOBUF_protobuf_5ftests_2eproto__INCLUDED
++
++#include <string>
++
++#include <google/protobuf/stubs/common.h>
++
++#if GOOGLE_PROTOBUF_VERSION < 2004000
++#error This file was generated by a newer version of protoc which is
++#error incompatible with your Protocol Buffer headers. Please update
++#error your headers.
++#endif
++#if 2004001 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION
++#error This file was generated by an older version of protoc which is
++#error incompatible with your Protocol Buffer headers. Please
++#error regenerate this file with a newer version of protoc.
++#endif
++
++#include <google/protobuf/generated_message_util.h>
++#include <google/protobuf/repeated_field.h>
++#include <google/protobuf/extension_set.h>
++#include <google/protobuf/generated_message_reflection.h>
++// @@protoc_insertion_point(includes)
++
++namespace tests {
++
++// Internal implementation detail -- do not call these.
++void protobuf_AddDesc_protobuf_5ftests_2eproto();
++void protobuf_AssignDesc_protobuf_5ftests_2eproto();
++void protobuf_ShutdownFile_protobuf_5ftests_2eproto();
++
++class Nested;
++class Message;
++
++enum Enum {
++ ONE = 1,
++ TWO = 2
++};
++bool Enum_IsValid(int value);
++const Enum Enum_MIN = ONE;
++const Enum Enum_MAX = TWO;
++const int Enum_ARRAYSIZE = Enum_MAX + 1;
++
++const ::google::protobuf::EnumDescriptor* Enum_descriptor();
++inline const ::std::string& Enum_Name(Enum value) {
++ return ::google::protobuf::internal::NameOfEnum(
++ Enum_descriptor(), value);
++}
++inline bool Enum_Parse(
++ const ::std::string& name, Enum* value) {
++ return ::google::protobuf::internal::ParseNamedEnum<Enum>(
++ Enum_descriptor(), name, value);
++}
++// ===================================================================
++
++class Nested : public ::google::protobuf::Message {
++ public:
++ Nested();
++ virtual ~Nested();
++
++ Nested(const Nested& from);
++
++ inline Nested& operator=(const Nested& from) {
++ CopyFrom(from);
++ return *this;
++ }
++
++ inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {
++ return _unknown_fields_;
++ }
++
++ inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {
++ return &_unknown_fields_;
++ }
++
++ static const ::google::protobuf::Descriptor* descriptor();
++ static const Nested& default_instance();
++
++ void Swap(Nested* other);
++
++ // implements Message ----------------------------------------------
++
++ Nested* New() const;
++ void CopyFrom(const ::google::protobuf::Message& from);
++ void MergeFrom(const ::google::protobuf::Message& from);
++ void CopyFrom(const Nested& from);
++ void MergeFrom(const Nested& from);
++ void Clear();
++ bool IsInitialized() const;
++
++ int ByteSize() const;
++ bool MergePartialFromCodedStream(
++ ::google::protobuf::io::CodedInputStream* input);
++ void SerializeWithCachedSizes(
++ ::google::protobuf::io::CodedOutputStream* output) const;
++ ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;
++ int GetCachedSize() const { return _cached_size_; }
++ private:
++ void SharedCtor();
++ void SharedDtor();
++ void SetCachedSize(int size) const;
++ public:
++
++ ::google::protobuf::Metadata GetMetadata() const;
++
++ // nested types ----------------------------------------------------
++
++ // accessors -------------------------------------------------------
++
++ // optional string str = 1;
++ inline bool has_str() const;
++ inline void clear_str();
++ static const int kStrFieldNumber = 1;
++ inline const ::std::string& str() const;
++ inline void set_str(const ::std::string& value);
++ inline void set_str(const char* value);
++ inline void set_str(const char* value, size_t size);
++ inline ::std::string* mutable_str();
++ inline ::std::string* release_str();
++
++ // @@protoc_insertion_point(class_scope:tests.Nested)
++ private:
++ inline void set_has_str();
++ inline void clear_has_str();
++
++ ::google::protobuf::UnknownFieldSet _unknown_fields_;
++
++ ::std::string* str_;
++
++ mutable int _cached_size_;
++ ::google::protobuf::uint32 _has_bits_[(1 + 31) / 32];
++
++ friend void protobuf_AddDesc_protobuf_5ftests_2eproto();
++ friend void protobuf_AssignDesc_protobuf_5ftests_2eproto();
++ friend void protobuf_ShutdownFile_protobuf_5ftests_2eproto();
++
++ void InitAsDefaultInstance();
++ static Nested* default_instance_;
++};
++// -------------------------------------------------------------------
++
++class Message : public ::google::protobuf::Message {
++ public:
++ Message();
++ virtual ~Message();
++
++ Message(const Message& from);
++
++ inline Message& operator=(const Message& from) {
++ CopyFrom(from);
++ return *this;
++ }
++
++ inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const {
++ return _unknown_fields_;
++ }
++
++ inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() {
++ return &_unknown_fields_;
++ }
++
++ static const ::google::protobuf::Descriptor* descriptor();
++ static const Message& default_instance();
++
++ void Swap(Message* other);
++
++ // implements Message ----------------------------------------------
++
++ Message* New() const;
++ void CopyFrom(const ::google::protobuf::Message& from);
++ void MergeFrom(const ::google::protobuf::Message& from);
++ void CopyFrom(const Message& from);
++ void MergeFrom(const Message& from);
++ void Clear();
++ bool IsInitialized() const;
++
++ int ByteSize() const;
++ bool MergePartialFromCodedStream(
++ ::google::protobuf::io::CodedInputStream* input);
++ void SerializeWithCachedSizes(
++ ::google::protobuf::io::CodedOutputStream* output) const;
++ ::google::protobuf::uint8* SerializeWithCachedSizesToArray(::google::protobuf::uint8* output) const;
++ int GetCachedSize() const { return _cached_size_; }
++ private:
++ void SharedCtor();
++ void SharedDtor();
++ void SetCachedSize(int size) const;
++ public:
++
++ ::google::protobuf::Metadata GetMetadata() const;
++
++ // nested types ----------------------------------------------------
++
++ // accessors -------------------------------------------------------
++
++ // required string str = 1;
++ inline bool has_str() const;
++ inline void clear_str();
++ static const int kStrFieldNumber = 1;
++ inline const ::std::string& str() const;
++ inline void set_str(const ::std::string& value);
++ inline void set_str(const char* value);
++ inline void set_str(const char* value, size_t size);
++ inline ::std::string* mutable_str();
++ inline ::std::string* release_str();
++
++ // required bytes bytes = 2;
++ inline bool has_bytes() const;
++ inline void clear_bytes();
++ static const int kBytesFieldNumber = 2;
++ inline const ::std::string& bytes() const;
++ inline void set_bytes(const ::std::string& value);
++ inline void set_bytes(const char* value);
++ inline void set_bytes(const void* value, size_t size);
++ inline ::std::string* mutable_bytes();
++ inline ::std::string* release_bytes();
++
++ // optional int32 int32 = 3;
++ inline bool has_int32() const;
++ inline void clear_int32();
++ static const int kInt32FieldNumber = 3;
++ inline ::google::protobuf::int32 int32() const;
++ inline void set_int32(::google::protobuf::int32 value);
++
++ // optional int64 int64 = 4;
++ inline bool has_int64() const;
++ inline void clear_int64();
++ static const int kInt64FieldNumber = 4;
++ inline ::google::protobuf::int64 int64() const;
++ inline void set_int64(::google::protobuf::int64 value);
++
++ // optional uint32 uint32 = 5;
++ inline bool has_uint32() const;
++ inline void clear_uint32();
++ static const int kUint32FieldNumber = 5;
++ inline ::google::protobuf::uint32 uint32() const;
++ inline void set_uint32(::google::protobuf::uint32 value);
++
++ // optional uint64 uint64 = 6;
++ inline bool has_uint64() const;
++ inline void clear_uint64();
++ static const int kUint64FieldNumber = 6;
++ inline ::google::protobuf::uint64 uint64() const;
++ inline void set_uint64(::google::protobuf::uint64 value);
++
++ // optional sint32 sint32 = 7;
++ inline bool has_sint32() const;
++ inline void clear_sint32();
++ static const int kSint32FieldNumber = 7;
++ inline ::google::protobuf::int32 sint32() const;
++ inline void set_sint32(::google::protobuf::int32 value);
++
++ // optional sint64 sint64 = 8;
++ inline bool has_sint64() const;
++ inline void clear_sint64();
++ static const int kSint64FieldNumber = 8;
++ inline ::google::protobuf::int64 sint64() const;
++ inline void set_sint64(::google::protobuf::int64 value);
++
++ // required float f = 9;
++ inline bool has_f() const;
++ inline void clear_f();
++ static const int kFFieldNumber = 9;
++ inline float f() const;
++ inline void set_f(float value);
++
++ // required double d = 10;
++ inline bool has_d() const;
++ inline void clear_d();
++ static const int kDFieldNumber = 10;
++ inline double d() const;
++ inline void set_d(double value);
++
++ // required .tests.Enum e = 11;
++ inline bool has_e() const;
++ inline void clear_e();
++ static const int kEFieldNumber = 11;
++ inline tests::Enum e() const;
++ inline void set_e(tests::Enum value);
++
++ // required .tests.Nested nested = 12;
++ inline bool has_nested() const;
++ inline void clear_nested();
++ static const int kNestedFieldNumber = 12;
++ inline const ::tests::Nested& nested() const;
++ inline ::tests::Nested* mutable_nested();
++ inline ::tests::Nested* release_nested();
++
++ // repeated string repeated_string = 13;
++ inline int repeated_string_size() const;
++ inline void clear_repeated_string();
++ static const int kRepeatedStringFieldNumber = 13;
++ inline const ::std::string& repeated_string(int index) const;
++ inline ::std::string* mutable_repeated_string(int index);
++ inline void set_repeated_string(int index, const ::std::string& value);
++ inline void set_repeated_string(int index, const char* value);
++ inline void set_repeated_string(int index, const char* value, size_t size);
++ inline ::std::string* add_repeated_string();
++ inline void add_repeated_string(const ::std::string& value);
++ inline void add_repeated_string(const char* value);
++ inline void add_repeated_string(const char* value, size_t size);
++ inline const ::google::protobuf::RepeatedPtrField< ::std::string>& repeated_string() const;
++ inline ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_repeated_string();
++
++ // repeated bytes repeated_bytes = 14;
++ inline int repeated_bytes_size() const;
++ inline void clear_repeated_bytes();
++ static const int kRepeatedBytesFieldNumber = 14;
++ inline const ::std::string& repeated_bytes(int index) const;
++ inline ::std::string* mutable_repeated_bytes(int index);
++ inline void set_repeated_bytes(int index, const ::std::string& value);
++ inline void set_repeated_bytes(int index, const char* value);
++ inline void set_repeated_bytes(int index, const void* value, size_t size);
++ inline ::std::string* add_repeated_bytes();
++ inline void add_repeated_bytes(const ::std::string& value);
++ inline void add_repeated_bytes(const char* value);
++ inline void add_repeated_bytes(const void* value, size_t size);
++ inline const ::google::protobuf::RepeatedPtrField< ::std::string>& repeated_bytes() const;
++ inline ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_repeated_bytes();
++
++ // repeated int32 repeated_int32 = 15;
++ inline int repeated_int32_size() const;
++ inline void clear_repeated_int32();
++ static const int kRepeatedInt32FieldNumber = 15;
++ inline ::google::protobuf::int32 repeated_int32(int index) const;
++ inline void set_repeated_int32(int index, ::google::protobuf::int32 value);
++ inline void add_repeated_int32(::google::protobuf::int32 value);
++ inline const ::google::protobuf::RepeatedField< ::google::protobuf::int32 >&
++ repeated_int32() const;
++ inline ::google::protobuf::RepeatedField< ::google::protobuf::int32 >*
++ mutable_repeated_int32();
++
++ // repeated int64 repeated_int64 = 16;
++ inline int repeated_int64_size() const;
++ inline void clear_repeated_int64();
++ static const int kRepeatedInt64FieldNumber = 16;
++ inline ::google::protobuf::int64 repeated_int64(int index) const;
++ inline void set_repeated_int64(int index, ::google::protobuf::int64 value);
++ inline void add_repeated_int64(::google::protobuf::int64 value);
++ inline const ::google::protobuf::RepeatedField< ::google::protobuf::int64 >&
++ repeated_int64() const;
++ inline ::google::protobuf::RepeatedField< ::google::protobuf::int64 >*
++ mutable_repeated_int64();
++
++ // repeated uint32 repeated_uint32 = 17;
++ inline int repeated_uint32_size() const;
++ inline void clear_repeated_uint32();
++ static const int kRepeatedUint32FieldNumber = 17;
++ inline ::google::protobuf::uint32 repeated_uint32(int index) const;
++ inline void set_repeated_uint32(int index, ::google::protobuf::uint32 value);
++ inline void add_repeated_uint32(::google::protobuf::uint32 value);
++ inline const ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >&
++ repeated_uint32() const;
++ inline ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >*
++ mutable_repeated_uint32();
++
++ // repeated uint64 repeated_uint64 = 18;
++ inline int repeated_uint64_size() const;
++ inline void clear_repeated_uint64();
++ static const int kRepeatedUint64FieldNumber = 18;
++ inline ::google::protobuf::uint64 repeated_uint64(int index) const;
++ inline void set_repeated_uint64(int index, ::google::protobuf::uint64 value);
++ inline void add_repeated_uint64(::google::protobuf::uint64 value);
++ inline const ::google::protobuf::RepeatedField< ::google::protobuf::uint64 >&
++ repeated_uint64() const;
++ inline ::google::protobuf::RepeatedField< ::google::protobuf::uint64 >*
++ mutable_repeated_uint64();
++
++ // repeated sint32 repeated_sint32 = 19;
++ inline int repeated_sint32_size() const;
++ inline void clear_repeated_sint32();
++ static const int kRepeatedSint32FieldNumber = 19;
++ inline ::google::protobuf::int32 repeated_sint32(int index) const;
++ inline void set_repeated_sint32(int index, ::google::protobuf::int32 value);
++ inline void add_repeated_sint32(::google::protobuf::int32 value);
++ inline const ::google::protobuf::RepeatedField< ::google::protobuf::int32 >&
++ repeated_sint32() const;
++ inline ::google::protobuf::RepeatedField< ::google::protobuf::int32 >*
++ mutable_repeated_sint32();
++
++ // repeated sint64 repeated_sint64 = 20;
++ inline int repeated_sint64_size() const;
++ inline void clear_repeated_sint64();
++ static const int kRepeatedSint64FieldNumber = 20;
++ inline ::google::protobuf::int64 repeated_sint64(int index) const;
++ inline void set_repeated_sint64(int index, ::google::protobuf::int64 value);
++ inline void add_repeated_sint64(::google::protobuf::int64 value);
++ inline const ::google::protobuf::RepeatedField< ::google::protobuf::int64 >&
++ repeated_sint64() const;
++ inline ::google::protobuf::RepeatedField< ::google::protobuf::int64 >*
++ mutable_repeated_sint64();
++
++ // repeated float repeated_float = 21;
++ inline int repeated_float_size() const;
++ inline void clear_repeated_float();
++ static const int kRepeatedFloatFieldNumber = 21;
++ inline float repeated_float(int index) const;
++ inline void set_repeated_float(int index, float value);
++ inline void add_repeated_float(float value);
++ inline const ::google::protobuf::RepeatedField< float >&
++ repeated_float() const;
++ inline ::google::protobuf::RepeatedField< float >*
++ mutable_repeated_float();
++
++ // repeated double repeated_double = 22;
++ inline int repeated_double_size() const;
++ inline void clear_repeated_double();
++ static const int kRepeatedDoubleFieldNumber = 22;
++ inline double repeated_double(int index) const;
++ inline void set_repeated_double(int index, double value);
++ inline void add_repeated_double(double value);
++ inline const ::google::protobuf::RepeatedField< double >&
++ repeated_double() const;
++ inline ::google::protobuf::RepeatedField< double >*
++ mutable_repeated_double();
++
++ // repeated .tests.Enum repeated_enum = 23;
++ inline int repeated_enum_size() const;
++ inline void clear_repeated_enum();
++ static const int kRepeatedEnumFieldNumber = 23;
++ inline tests::Enum repeated_enum(int index) const;
++ inline void set_repeated_enum(int index, tests::Enum value);
++ inline void add_repeated_enum(tests::Enum value);
++ inline const ::google::protobuf::RepeatedField<int>& repeated_enum() const;
++ inline ::google::protobuf::RepeatedField<int>* mutable_repeated_enum();
++
++ // repeated .tests.Nested repeated_nested = 24;
++ inline int repeated_nested_size() const;
++ inline void clear_repeated_nested();
++ static const int kRepeatedNestedFieldNumber = 24;
++ inline const ::tests::Nested& repeated_nested(int index) const;
++ inline ::tests::Nested* mutable_repeated_nested(int index);
++ inline ::tests::Nested* add_repeated_nested();
++ inline const ::google::protobuf::RepeatedPtrField< ::tests::Nested >&
++ repeated_nested() const;
++ inline ::google::protobuf::RepeatedPtrField< ::tests::Nested >*
++ mutable_repeated_nested();
++
++ // repeated string empty = 25;
++ inline int empty_size() const;
++ inline void clear_empty();
++ static const int kEmptyFieldNumber = 25;
++ inline const ::std::string& empty(int index) const;
++ inline ::std::string* mutable_empty(int index);
++ inline void set_empty(int index, const ::std::string& value);
++ inline void set_empty(int index, const char* value);
++ inline void set_empty(int index, const char* value, size_t size);
++ inline ::std::string* add_empty();
++ inline void add_empty(const ::std::string& value);
++ inline void add_empty(const char* value);
++ inline void add_empty(const char* value, size_t size);
++ inline const ::google::protobuf::RepeatedPtrField< ::std::string>& empty() const;
++ inline ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_empty();
++
++ // @@protoc_insertion_point(class_scope:tests.Message)
++ private:
++ inline void set_has_str();
++ inline void clear_has_str();
++ inline void set_has_bytes();
++ inline void clear_has_bytes();
++ inline void set_has_int32();
++ inline void clear_has_int32();
++ inline void set_has_int64();
++ inline void clear_has_int64();
++ inline void set_has_uint32();
++ inline void clear_has_uint32();
++ inline void set_has_uint64();
++ inline void clear_has_uint64();
++ inline void set_has_sint32();
++ inline void clear_has_sint32();
++ inline void set_has_sint64();
++ inline void clear_has_sint64();
++ inline void set_has_f();
++ inline void clear_has_f();
++ inline void set_has_d();
++ inline void clear_has_d();
++ inline void set_has_e();
++ inline void clear_has_e();
++ inline void set_has_nested();
++ inline void clear_has_nested();
++
++ ::google::protobuf::UnknownFieldSet _unknown_fields_;
++
++ ::std::string* str_;
++ ::std::string* bytes_;
++ ::google::protobuf::int64 int64_;
++ ::google::protobuf::int32 int32_;
++ ::google::protobuf::uint32 uint32_;
++ ::google::protobuf::uint64 uint64_;
++ ::google::protobuf::int64 sint64_;
++ ::google::protobuf::int32 sint32_;
++ float f_;
++ double d_;
++ ::tests::Nested* nested_;
++ ::google::protobuf::RepeatedPtrField< ::std::string> repeated_string_;
++ ::google::protobuf::RepeatedPtrField< ::std::string> repeated_bytes_;
++ ::google::protobuf::RepeatedField< ::google::protobuf::int32 > repeated_int32_;
++ ::google::protobuf::RepeatedField< ::google::protobuf::int64 > repeated_int64_;
++ ::google::protobuf::RepeatedField< ::google::protobuf::uint32 > repeated_uint32_;
++ ::google::protobuf::RepeatedField< ::google::protobuf::uint64 > repeated_uint64_;
++ ::google::protobuf::RepeatedField< ::google::protobuf::int32 > repeated_sint32_;
++ ::google::protobuf::RepeatedField< ::google::protobuf::int64 > repeated_sint64_;
++ ::google::protobuf::RepeatedField< float > repeated_float_;
++ ::google::protobuf::RepeatedField< double > repeated_double_;
++ ::google::protobuf::RepeatedField<int> repeated_enum_;
++ ::google::protobuf::RepeatedPtrField< ::tests::Nested > repeated_nested_;
++ ::google::protobuf::RepeatedPtrField< ::std::string> empty_;
++ int e_;
++
++ mutable int _cached_size_;
++ ::google::protobuf::uint32 _has_bits_[(25 + 31) / 32];
++
++ friend void protobuf_AddDesc_protobuf_5ftests_2eproto();
++ friend void protobuf_AssignDesc_protobuf_5ftests_2eproto();
++ friend void protobuf_ShutdownFile_protobuf_5ftests_2eproto();
++
++ void InitAsDefaultInstance();
++ static Message* default_instance_;
++};
++// ===================================================================
++
++
++// ===================================================================
++
++// Nested
++
++// optional string str = 1;
++inline bool Nested::has_str() const {
++ return (_has_bits_[0] & 0x00000001u) != 0;
++}
++inline void Nested::set_has_str() {
++ _has_bits_[0] |= 0x00000001u;
++}
++inline void Nested::clear_has_str() {
++ _has_bits_[0] &= ~0x00000001u;
++}
++inline void Nested::clear_str() {
++ if (str_ != &::google::protobuf::internal::kEmptyString) {
++ str_->clear();
++ }
++ clear_has_str();
++}
++inline const ::std::string& Nested::str() const {
++ return *str_;
++}
++inline void Nested::set_str(const ::std::string& value) {
++ set_has_str();
++ if (str_ == &::google::protobuf::internal::kEmptyString) {
++ str_ = new ::std::string;
++ }
++ str_->assign(value);
++}
++inline void Nested::set_str(const char* value) {
++ set_has_str();
++ if (str_ == &::google::protobuf::internal::kEmptyString) {
++ str_ = new ::std::string;
++ }
++ str_->assign(value);
++}
++inline void Nested::set_str(const char* value, size_t size) {
++ set_has_str();
++ if (str_ == &::google::protobuf::internal::kEmptyString) {
++ str_ = new ::std::string;
++ }
++ str_->assign(reinterpret_cast<const char*>(value), size);
++}
++inline ::std::string* Nested::mutable_str() {
++ set_has_str();
++ if (str_ == &::google::protobuf::internal::kEmptyString) {
++ str_ = new ::std::string;
++ }
++ return str_;
++}
++inline ::std::string* Nested::release_str() {
++ clear_has_str();
++ if (str_ == &::google::protobuf::internal::kEmptyString) {
++ return NULL;
++ } else {
++ ::std::string* temp = str_;
++ str_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString);
++ return temp;
++ }
++}
++
++// -------------------------------------------------------------------
++
++// Message
++
++// required string str = 1;
++inline bool Message::has_str() const {
++ return (_has_bits_[0] & 0x00000001u) != 0;
++}
++inline void Message::set_has_str() {
++ _has_bits_[0] |= 0x00000001u;
++}
++inline void Message::clear_has_str() {
++ _has_bits_[0] &= ~0x00000001u;
++}
++inline void Message::clear_str() {
++ if (str_ != &::google::protobuf::internal::kEmptyString) {
++ str_->clear();
++ }
++ clear_has_str();
++}
++inline const ::std::string& Message::str() const {
++ return *str_;
++}
++inline void Message::set_str(const ::std::string& value) {
++ set_has_str();
++ if (str_ == &::google::protobuf::internal::kEmptyString) {
++ str_ = new ::std::string;
++ }
++ str_->assign(value);
++}
++inline void Message::set_str(const char* value) {
++ set_has_str();
++ if (str_ == &::google::protobuf::internal::kEmptyString) {
++ str_ = new ::std::string;
++ }
++ str_->assign(value);
++}
++inline void Message::set_str(const char* value, size_t size) {
++ set_has_str();
++ if (str_ == &::google::protobuf::internal::kEmptyString) {
++ str_ = new ::std::string;
++ }
++ str_->assign(reinterpret_cast<const char*>(value), size);
++}
++inline ::std::string* Message::mutable_str() {
++ set_has_str();
++ if (str_ == &::google::protobuf::internal::kEmptyString) {
++ str_ = new ::std::string;
++ }
++ return str_;
++}
++inline ::std::string* Message::release_str() {
++ clear_has_str();
++ if (str_ == &::google::protobuf::internal::kEmptyString) {
++ return NULL;
++ } else {
++ ::std::string* temp = str_;
++ str_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString);
++ return temp;
++ }
++}
++
++// required bytes bytes = 2;
++inline bool Message::has_bytes() const {
++ return (_has_bits_[0] & 0x00000002u) != 0;
++}
++inline void Message::set_has_bytes() {
++ _has_bits_[0] |= 0x00000002u;
++}
++inline void Message::clear_has_bytes() {
++ _has_bits_[0] &= ~0x00000002u;
++}
++inline void Message::clear_bytes() {
++ if (bytes_ != &::google::protobuf::internal::kEmptyString) {
++ bytes_->clear();
++ }
++ clear_has_bytes();
++}
++inline const ::std::string& Message::bytes() const {
++ return *bytes_;
++}
++inline void Message::set_bytes(const ::std::string& value) {
++ set_has_bytes();
++ if (bytes_ == &::google::protobuf::internal::kEmptyString) {
++ bytes_ = new ::std::string;
++ }
++ bytes_->assign(value);
++}
++inline void Message::set_bytes(const char* value) {
++ set_has_bytes();
++ if (bytes_ == &::google::protobuf::internal::kEmptyString) {
++ bytes_ = new ::std::string;
++ }
++ bytes_->assign(value);
++}
++inline void Message::set_bytes(const void* value, size_t size) {
++ set_has_bytes();
++ if (bytes_ == &::google::protobuf::internal::kEmptyString) {
++ bytes_ = new ::std::string;
++ }
++ bytes_->assign(reinterpret_cast<const char*>(value), size);
++}
++inline ::std::string* Message::mutable_bytes() {
++ set_has_bytes();
++ if (bytes_ == &::google::protobuf::internal::kEmptyString) {
++ bytes_ = new ::std::string;
++ }
++ return bytes_;
++}
++inline ::std::string* Message::release_bytes() {
++ clear_has_bytes();
++ if (bytes_ == &::google::protobuf::internal::kEmptyString) {
++ return NULL;
++ } else {
++ ::std::string* temp = bytes_;
++ bytes_ = const_cast< ::std::string*>(&::google::protobuf::internal::kEmptyString);
++ return temp;
++ }
++}
++
++// optional int32 int32 = 3;
++inline bool Message::has_int32() const {
++ return (_has_bits_[0] & 0x00000004u) != 0;
++}
++inline void Message::set_has_int32() {
++ _has_bits_[0] |= 0x00000004u;
++}
++inline void Message::clear_has_int32() {
++ _has_bits_[0] &= ~0x00000004u;
++}
++inline void Message::clear_int32() {
++ int32_ = 0;
++ clear_has_int32();
++}
++inline ::google::protobuf::int32 Message::int32() const {
++ return int32_;
++}
++inline void Message::set_int32(::google::protobuf::int32 value) {
++ set_has_int32();
++ int32_ = value;
++}
++
++// optional int64 int64 = 4;
++inline bool Message::has_int64() const {
++ return (_has_bits_[0] & 0x00000008u) != 0;
++}
++inline void Message::set_has_int64() {
++ _has_bits_[0] |= 0x00000008u;
++}
++inline void Message::clear_has_int64() {
++ _has_bits_[0] &= ~0x00000008u;
++}
++inline void Message::clear_int64() {
++ int64_ = GOOGLE_LONGLONG(0);
++ clear_has_int64();
++}
++inline ::google::protobuf::int64 Message::int64() const {
++ return int64_;
++}
++inline void Message::set_int64(::google::protobuf::int64 value) {
++ set_has_int64();
++ int64_ = value;
++}
++
++// optional uint32 uint32 = 5;
++inline bool Message::has_uint32() const {
++ return (_has_bits_[0] & 0x00000010u) != 0;
++}
++inline void Message::set_has_uint32() {
++ _has_bits_[0] |= 0x00000010u;
++}
++inline void Message::clear_has_uint32() {
++ _has_bits_[0] &= ~0x00000010u;
++}
++inline void Message::clear_uint32() {
++ uint32_ = 0u;
++ clear_has_uint32();
++}
++inline ::google::protobuf::uint32 Message::uint32() const {
++ return uint32_;
++}
++inline void Message::set_uint32(::google::protobuf::uint32 value) {
++ set_has_uint32();
++ uint32_ = value;
++}
++
++// optional uint64 uint64 = 6;
++inline bool Message::has_uint64() const {
++ return (_has_bits_[0] & 0x00000020u) != 0;
++}
++inline void Message::set_has_uint64() {
++ _has_bits_[0] |= 0x00000020u;
++}
++inline void Message::clear_has_uint64() {
++ _has_bits_[0] &= ~0x00000020u;
++}
++inline void Message::clear_uint64() {
++ uint64_ = GOOGLE_ULONGLONG(0);
++ clear_has_uint64();
++}
++inline ::google::protobuf::uint64 Message::uint64() const {
++ return uint64_;
++}
++inline void Message::set_uint64(::google::protobuf::uint64 value) {
++ set_has_uint64();
++ uint64_ = value;
++}
++
++// optional sint32 sint32 = 7;
++inline bool Message::has_sint32() const {
++ return (_has_bits_[0] & 0x00000040u) != 0;
++}
++inline void Message::set_has_sint32() {
++ _has_bits_[0] |= 0x00000040u;
++}
++inline void Message::clear_has_sint32() {
++ _has_bits_[0] &= ~0x00000040u;
++}
++inline void Message::clear_sint32() {
++ sint32_ = 0;
++ clear_has_sint32();
++}
++inline ::google::protobuf::int32 Message::sint32() const {
++ return sint32_;
++}
++inline void Message::set_sint32(::google::protobuf::int32 value) {
++ set_has_sint32();
++ sint32_ = value;
++}
++
++// optional sint64 sint64 = 8;
++inline bool Message::has_sint64() const {
++ return (_has_bits_[0] & 0x00000080u) != 0;
++}
++inline void Message::set_has_sint64() {
++ _has_bits_[0] |= 0x00000080u;
++}
++inline void Message::clear_has_sint64() {
++ _has_bits_[0] &= ~0x00000080u;
++}
++inline void Message::clear_sint64() {
++ sint64_ = GOOGLE_LONGLONG(0);
++ clear_has_sint64();
++}
++inline ::google::protobuf::int64 Message::sint64() const {
++ return sint64_;
++}
++inline void Message::set_sint64(::google::protobuf::int64 value) {
++ set_has_sint64();
++ sint64_ = value;
++}
++
++// required float f = 9;
++inline bool Message::has_f() const {
++ return (_has_bits_[0] & 0x00000100u) != 0;
++}
++inline void Message::set_has_f() {
++ _has_bits_[0] |= 0x00000100u;
++}
++inline void Message::clear_has_f() {
++ _has_bits_[0] &= ~0x00000100u;
++}
++inline void Message::clear_f() {
++ f_ = 0;
++ clear_has_f();
++}
++inline float Message::f() const {
++ return f_;
++}
++inline void Message::set_f(float value) {
++ set_has_f();
++ f_ = value;
++}
++
++// required double d = 10;
++inline bool Message::has_d() const {
++ return (_has_bits_[0] & 0x00000200u) != 0;
++}
++inline void Message::set_has_d() {
++ _has_bits_[0] |= 0x00000200u;
++}
++inline void Message::clear_has_d() {
++ _has_bits_[0] &= ~0x00000200u;
++}
++inline void Message::clear_d() {
++ d_ = 0;
++ clear_has_d();
++}
++inline double Message::d() const {
++ return d_;
++}
++inline void Message::set_d(double value) {
++ set_has_d();
++ d_ = value;
++}
++
++// required .tests.Enum e = 11;
++inline bool Message::has_e() const {
++ return (_has_bits_[0] & 0x00000400u) != 0;
++}
++inline void Message::set_has_e() {
++ _has_bits_[0] |= 0x00000400u;
++}
++inline void Message::clear_has_e() {
++ _has_bits_[0] &= ~0x00000400u;
++}
++inline void Message::clear_e() {
++ e_ = 1;
++ clear_has_e();
++}
++inline tests::Enum Message::e() const {
++ return static_cast< tests::Enum >(e_);
++}
++inline void Message::set_e(tests::Enum value) {
++ GOOGLE_DCHECK(tests::Enum_IsValid(value));
++ set_has_e();
++ e_ = value;
++}
++
++// required .tests.Nested nested = 12;
++inline bool Message::has_nested() const {
++ return (_has_bits_[0] & 0x00000800u) != 0;
++}
++inline void Message::set_has_nested() {
++ _has_bits_[0] |= 0x00000800u;
++}
++inline void Message::clear_has_nested() {
++ _has_bits_[0] &= ~0x00000800u;
++}
++inline void Message::clear_nested() {
++ if (nested_ != NULL) nested_->::tests::Nested::Clear();
++ clear_has_nested();
++}
++inline const ::tests::Nested& Message::nested() const {
++ return nested_ != NULL ? *nested_ : *default_instance_->nested_;
++}
++inline ::tests::Nested* Message::mutable_nested() {
++ set_has_nested();
++ if (nested_ == NULL) nested_ = new ::tests::Nested;
++ return nested_;
++}
++inline ::tests::Nested* Message::release_nested() {
++ clear_has_nested();
++ ::tests::Nested* temp = nested_;
++ nested_ = NULL;
++ return temp;
++}
++
++// repeated string repeated_string = 13;
++inline int Message::repeated_string_size() const {
++ return repeated_string_.size();
++}
++inline void Message::clear_repeated_string() {
++ repeated_string_.Clear();
++}
++inline const ::std::string& Message::repeated_string(int index) const {
++ return repeated_string_.Get(index);
++}
++inline ::std::string* Message::mutable_repeated_string(int index) {
++ return repeated_string_.Mutable(index);
++}
++inline void Message::set_repeated_string(int index, const ::std::string& value) {
++ repeated_string_.Mutable(index)->assign(value);
++}
++inline void Message::set_repeated_string(int index, const char* value) {
++ repeated_string_.Mutable(index)->assign(value);
++}
++inline void Message::set_repeated_string(int index, const char* value, size_t size) {
++ repeated_string_.Mutable(index)->assign(
++ reinterpret_cast<const char*>(value), size);
++}
++inline ::std::string* Message::add_repeated_string() {
++ return repeated_string_.Add();
++}
++inline void Message::add_repeated_string(const ::std::string& value) {
++ repeated_string_.Add()->assign(value);
++}
++inline void Message::add_repeated_string(const char* value) {
++ repeated_string_.Add()->assign(value);
++}
++inline void Message::add_repeated_string(const char* value, size_t size) {
++ repeated_string_.Add()->assign(reinterpret_cast<const char*>(value), size);
++}
++inline const ::google::protobuf::RepeatedPtrField< ::std::string>&
++Message::repeated_string() const {
++ return repeated_string_;
++}
++inline ::google::protobuf::RepeatedPtrField< ::std::string>*
++Message::mutable_repeated_string() {
++ return &repeated_string_;
++}
++
++// repeated bytes repeated_bytes = 14;
++inline int Message::repeated_bytes_size() const {
++ return repeated_bytes_.size();
++}
++inline void Message::clear_repeated_bytes() {
++ repeated_bytes_.Clear();
++}
++inline const ::std::string& Message::repeated_bytes(int index) const {
++ return repeated_bytes_.Get(index);
++}
++inline ::std::string* Message::mutable_repeated_bytes(int index) {
++ return repeated_bytes_.Mutable(index);
++}
++inline void Message::set_repeated_bytes(int index, const ::std::string& value) {
++ repeated_bytes_.Mutable(index)->assign(value);
++}
++inline void Message::set_repeated_bytes(int index, const char* value) {
++ repeated_bytes_.Mutable(index)->assign(value);
++}
++inline void Message::set_repeated_bytes(int index, const void* value, size_t size) {
++ repeated_bytes_.Mutable(index)->assign(
++ reinterpret_cast<const char*>(value), size);
++}
++inline ::std::string* Message::add_repeated_bytes() {
++ return repeated_bytes_.Add();
++}
++inline void Message::add_repeated_bytes(const ::std::string& value) {
++ repeated_bytes_.Add()->assign(value);
++}
++inline void Message::add_repeated_bytes(const char* value) {
++ repeated_bytes_.Add()->assign(value);
++}
++inline void Message::add_repeated_bytes(const void* value, size_t size) {
++ repeated_bytes_.Add()->assign(reinterpret_cast<const char*>(value), size);
++}
++inline const ::google::protobuf::RepeatedPtrField< ::std::string>&
++Message::repeated_bytes() const {
++ return repeated_bytes_;
++}
++inline ::google::protobuf::RepeatedPtrField< ::std::string>*
++Message::mutable_repeated_bytes() {
++ return &repeated_bytes_;
++}
++
++// repeated int32 repeated_int32 = 15;
++inline int Message::repeated_int32_size() const {
++ return repeated_int32_.size();
++}
++inline void Message::clear_repeated_int32() {
++ repeated_int32_.Clear();
++}
++inline ::google::protobuf::int32 Message::repeated_int32(int index) const {
++ return repeated_int32_.Get(index);
++}
++inline void Message::set_repeated_int32(int index, ::google::protobuf::int32 value) {
++ repeated_int32_.Set(index, value);
++}
++inline void Message::add_repeated_int32(::google::protobuf::int32 value) {
++ repeated_int32_.Add(value);
++}
++inline const ::google::protobuf::RepeatedField< ::google::protobuf::int32 >&
++Message::repeated_int32() const {
++ return repeated_int32_;
++}
++inline ::google::protobuf::RepeatedField< ::google::protobuf::int32 >*
++Message::mutable_repeated_int32() {
++ return &repeated_int32_;
++}
++
++// repeated int64 repeated_int64 = 16;
++inline int Message::repeated_int64_size() const {
++ return repeated_int64_.size();
++}
++inline void Message::clear_repeated_int64() {
++ repeated_int64_.Clear();
++}
++inline ::google::protobuf::int64 Message::repeated_int64(int index) const {
++ return repeated_int64_.Get(index);
++}
++inline void Message::set_repeated_int64(int index, ::google::protobuf::int64 value) {
++ repeated_int64_.Set(index, value);
++}
++inline void Message::add_repeated_int64(::google::protobuf::int64 value) {
++ repeated_int64_.Add(value);
++}
++inline const ::google::protobuf::RepeatedField< ::google::protobuf::int64 >&
++Message::repeated_int64() const {
++ return repeated_int64_;
++}
++inline ::google::protobuf::RepeatedField< ::google::protobuf::int64 >*
++Message::mutable_repeated_int64() {
++ return &repeated_int64_;
++}
++
++// repeated uint32 repeated_uint32 = 17;
++inline int Message::repeated_uint32_size() const {
++ return repeated_uint32_.size();
++}
++inline void Message::clear_repeated_uint32() {
++ repeated_uint32_.Clear();
++}
++inline ::google::protobuf::uint32 Message::repeated_uint32(int index) const {
++ return repeated_uint32_.Get(index);
++}
++inline void Message::set_repeated_uint32(int index, ::google::protobuf::uint32 value) {
++ repeated_uint32_.Set(index, value);
++}
++inline void Message::add_repeated_uint32(::google::protobuf::uint32 value) {
++ repeated_uint32_.Add(value);
++}
++inline const ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >&
++Message::repeated_uint32() const {
++ return repeated_uint32_;
++}
++inline ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >*
++Message::mutable_repeated_uint32() {
++ return &repeated_uint32_;
++}
++
++// repeated uint64 repeated_uint64 = 18;
++inline int Message::repeated_uint64_size() const {
++ return repeated_uint64_.size();
++}
++inline void Message::clear_repeated_uint64() {
++ repeated_uint64_.Clear();
++}
++inline ::google::protobuf::uint64 Message::repeated_uint64(int index) const {
++ return repeated_uint64_.Get(index);
++}
++inline void Message::set_repeated_uint64(int index, ::google::protobuf::uint64 value) {
++ repeated_uint64_.Set(index, value);
++}
++inline void Message::add_repeated_uint64(::google::protobuf::uint64 value) {
++ repeated_uint64_.Add(value);
++}
++inline const ::google::protobuf::RepeatedField< ::google::protobuf::uint64 >&
++Message::repeated_uint64() const {
++ return repeated_uint64_;
++}
++inline ::google::protobuf::RepeatedField< ::google::protobuf::uint64 >*
++Message::mutable_repeated_uint64() {
++ return &repeated_uint64_;
++}
++
++// repeated sint32 repeated_sint32 = 19;
++inline int Message::repeated_sint32_size() const {
++ return repeated_sint32_.size();
++}
++inline void Message::clear_repeated_sint32() {
++ repeated_sint32_.Clear();
++}
++inline ::google::protobuf::int32 Message::repeated_sint32(int index) const {
++ return repeated_sint32_.Get(index);
++}
++inline void Message::set_repeated_sint32(int index, ::google::protobuf::int32 value) {
++ repeated_sint32_.Set(index, value);
++}
++inline void Message::add_repeated_sint32(::google::protobuf::int32 value) {
++ repeated_sint32_.Add(value);
++}
++inline const ::google::protobuf::RepeatedField< ::google::protobuf::int32 >&
++Message::repeated_sint32() const {
++ return repeated_sint32_;
++}
++inline ::google::protobuf::RepeatedField< ::google::protobuf::int32 >*
++Message::mutable_repeated_sint32() {
++ return &repeated_sint32_;
++}
++
++// repeated sint64 repeated_sint64 = 20;
++inline int Message::repeated_sint64_size() const {
++ return repeated_sint64_.size();
++}
++inline void Message::clear_repeated_sint64() {
++ repeated_sint64_.Clear();
++}
++inline ::google::protobuf::int64 Message::repeated_sint64(int index) const {
++ return repeated_sint64_.Get(index);
++}
++inline void Message::set_repeated_sint64(int index, ::google::protobuf::int64 value) {
++ repeated_sint64_.Set(index, value);
++}
++inline void Message::add_repeated_sint64(::google::protobuf::int64 value) {
++ repeated_sint64_.Add(value);
++}
++inline const ::google::protobuf::RepeatedField< ::google::protobuf::int64 >&
++Message::repeated_sint64() const {
++ return repeated_sint64_;
++}
++inline ::google::protobuf::RepeatedField< ::google::protobuf::int64 >*
++Message::mutable_repeated_sint64() {
++ return &repeated_sint64_;
++}
++
++// repeated float repeated_float = 21;
++inline int Message::repeated_float_size() const {
++ return repeated_float_.size();
++}
++inline void Message::clear_repeated_float() {
++ repeated_float_.Clear();
++}
++inline float Message::repeated_float(int index) const {
++ return repeated_float_.Get(index);
++}
++inline void Message::set_repeated_float(int index, float value) {
++ repeated_float_.Set(index, value);
++}
++inline void Message::add_repeated_float(float value) {
++ repeated_float_.Add(value);
++}
++inline const ::google::protobuf::RepeatedField< float >&
++Message::repeated_float() const {
++ return repeated_float_;
++}
++inline ::google::protobuf::RepeatedField< float >*
++Message::mutable_repeated_float() {
++ return &repeated_float_;
++}
++
++// repeated double repeated_double = 22;
++inline int Message::repeated_double_size() const {
++ return repeated_double_.size();
++}
++inline void Message::clear_repeated_double() {
++ repeated_double_.Clear();
++}
++inline double Message::repeated_double(int index) const {
++ return repeated_double_.Get(index);
++}
++inline void Message::set_repeated_double(int index, double value) {
++ repeated_double_.Set(index, value);
++}
++inline void Message::add_repeated_double(double value) {
++ repeated_double_.Add(value);
++}
++inline const ::google::protobuf::RepeatedField< double >&
++Message::repeated_double() const {
++ return repeated_double_;
++}
++inline ::google::protobuf::RepeatedField< double >*
++Message::mutable_repeated_double() {
++ return &repeated_double_;
++}
++
++// repeated .tests.Enum repeated_enum = 23;
++inline int Message::repeated_enum_size() const {
++ return repeated_enum_.size();
++}
++inline void Message::clear_repeated_enum() {
++ repeated_enum_.Clear();
++}
++inline tests::Enum Message::repeated_enum(int index) const {
++ return static_cast< tests::Enum >(repeated_enum_.Get(index));
++}
++inline void Message::set_repeated_enum(int index, tests::Enum value) {
++ GOOGLE_DCHECK(tests::Enum_IsValid(value));
++ repeated_enum_.Set(index, value);
++}
++inline void Message::add_repeated_enum(tests::Enum value) {
++ GOOGLE_DCHECK(tests::Enum_IsValid(value));
++ repeated_enum_.Add(value);
++}
++inline const ::google::protobuf::RepeatedField<int>&
++Message::repeated_enum() const {
++ return repeated_enum_;
++}
++inline ::google::protobuf::RepeatedField<int>*
++Message::mutable_repeated_enum() {
++ return &repeated_enum_;
++}
++
++// repeated .tests.Nested repeated_nested = 24;
++inline int Message::repeated_nested_size() const {
++ return repeated_nested_.size();
++}
++inline void Message::clear_repeated_nested() {
++ repeated_nested_.Clear();
++}
++inline const ::tests::Nested& Message::repeated_nested(int index) const {
++ return repeated_nested_.Get(index);
++}
++inline ::tests::Nested* Message::mutable_repeated_nested(int index) {
++ return repeated_nested_.Mutable(index);
++}
++inline ::tests::Nested* Message::add_repeated_nested() {
++ return repeated_nested_.Add();
++}
++inline const ::google::protobuf::RepeatedPtrField< ::tests::Nested >&
++Message::repeated_nested() const {
++ return repeated_nested_;
++}
++inline ::google::protobuf::RepeatedPtrField< ::tests::Nested >*
++Message::mutable_repeated_nested() {
++ return &repeated_nested_;
++}
++
++// repeated string empty = 25;
++inline int Message::empty_size() const {
++ return empty_.size();
++}
++inline void Message::clear_empty() {
++ empty_.Clear();
++}
++inline const ::std::string& Message::empty(int index) const {
++ return empty_.Get(index);
++}
++inline ::std::string* Message::mutable_empty(int index) {
++ return empty_.Mutable(index);
++}
++inline void Message::set_empty(int index, const ::std::string& value) {
++ empty_.Mutable(index)->assign(value);
++}
++inline void Message::set_empty(int index, const char* value) {
++ empty_.Mutable(index)->assign(value);
++}
++inline void Message::set_empty(int index, const char* value, size_t size) {
++ empty_.Mutable(index)->assign(
++ reinterpret_cast<const char*>(value), size);
++}
++inline ::std::string* Message::add_empty() {
++ return empty_.Add();
++}
++inline void Message::add_empty(const ::std::string& value) {
++ empty_.Add()->assign(value);
++}
++inline void Message::add_empty(const char* value) {
++ empty_.Add()->assign(value);
++}
++inline void Message::add_empty(const char* value, size_t size) {
++ empty_.Add()->assign(reinterpret_cast<const char*>(value), size);
++}
++inline const ::google::protobuf::RepeatedPtrField< ::std::string>&
++Message::empty() const {
++ return empty_;
++}
++inline ::google::protobuf::RepeatedPtrField< ::std::string>*
++Message::mutable_empty() {
++ return &empty_;
++}
++
++
++// @@protoc_insertion_point(namespace_scope)
++
++} // namespace tests
++
++#ifndef SWIG
++namespace google {
++namespace protobuf {
++
++template <>
++inline const EnumDescriptor* GetEnumDescriptor< tests::Enum>() {
++ return tests::Enum_descriptor();
++}
++
++} // namespace google
++} // namespace protobuf
++#endif // SWIG
++
++// @@protoc_insertion_point(global_scope)
++
++#endif // PROTOBUF_protobuf_5ftests_2eproto__INCLUDED
+diff --git a/src/tests/stout/protobuf_tests.proto b/src/tests/stout/protobuf_tests.proto
+new file mode 100644
+index 0000000..146cc20
+--- /dev/null
++++ b/src/tests/stout/protobuf_tests.proto
+@@ -0,0 +1,63 @@
++package tests;
++
++// NOTE: The generated headers for this file have been included
++// in the tests folder to simplify the build process (no need to
++// have protoc available to compile this file). As a result, if
++// there are any changes to this file, the headers must be
++// re-generated and committed alongside changes to this file.
++// There is a TODO in protobuf_tests.cpp that demonstrates how
++// to avoid the need for this file entirely by generating a
++// dynamic message at run-time.
++
++enum Enum {
++ ONE = 1;
++ TWO = 2;
++}
++
++
++message Nested {
++ optional string str = 1;
++}
++
++
++// An elaborate message for testing Proto->JSON conversion.
++message Message {
++ required string str = 1;
++
++ required bytes bytes = 2;
++
++ optional int32 int32 = 3;
++ optional int64 int64 = 4;
++ optional uint32 uint32 = 5;
++ optional uint64 uint64 = 6;
++ optional sint32 sint32 = 7;
++ optional sint64 sint64 = 8;
++
++ required float f = 9;
++ required double d = 10;
++
++ required Enum e = 11;
++
++ required Nested nested = 12;
++
++ repeated string repeated_string = 13;
++
++ repeated bytes repeated_bytes = 14;
++
++ repeated int32 repeated_int32 = 15;
++ repeated int64 repeated_int64 = 16;
++ repeated uint32 repeated_uint32 = 17;
++ repeated uint64 repeated_uint64 = 18;
++ repeated sint32 repeated_sint32 = 19;
++ repeated sint64 repeated_sint64 = 20;
++
++ repeated float repeated_float = 21;
++ repeated double repeated_double = 22;
++
++ repeated Enum repeated_enum = 23;
++
++ repeated Nested repeated_nested = 24;
++
++ repeated string empty = 25;
++}
++
+diff --git a/src/tests/stout/set_tests.cpp b/src/tests/stout/set_tests.cpp
+new file mode 100644
+index 0000000..cdedacd
+--- /dev/null
++++ b/src/tests/stout/set_tests.cpp
+@@ -0,0 +1,28 @@
++#include <gtest/gtest.h>
++
++#include <stout/set.hpp>
++
++TEST(Stout, Set)
++{
++ EXPECT_EQ(Set<int>(1, 2), Set<int>(1) | Set<int>(2));
++ EXPECT_EQ(Set<int>(1, 2), Set<int>(1, 2) | Set<int>(1));
++ EXPECT_EQ(Set<int>(1, 2), Set<int>(1) + 2);
++ EXPECT_EQ(Set<int>(1, 2), Set<int>(1, 2) + 2);
++ EXPECT_EQ(Set<int>(1, 2), Set<int>(1, 2, 3) & Set<int>(1, 2));
++ EXPECT_EQ(Set<int>(1, 2), Set<int>(1, 2) & Set<int>(1, 2));
++
++ Set<int> left;
++ left.insert(2);
++ left.insert(4);
++
++ Set<int> right;
++ right.insert(1);
++ right.insert(3);
++
++ EXPECT_EQ(Set<int>(1, 2, 3, 4), left | right);
++ EXPECT_EQ(Set<int>(), left & right);
++
++ std::set<int> s = left;
++
++ EXPECT_EQ(Set<int>(2, 4, 6), s + 6);
++}
+diff --git a/src/tests/stout/some_tests.cpp b/src/tests/stout/some_tests.cpp
+new file mode 100644
+index 0000000..4041dc4
+--- /dev/null
++++ b/src/tests/stout/some_tests.cpp
+@@ -0,0 +1,67 @@
++#include <gtest/gtest.h>
++
++#include <map>
++#include <string>
++
++#include <stout/gtest.hpp>
++#include <stout/none.hpp>
++#include <stout/option.hpp>
++#include <stout/result.hpp>
++#include <stout/some.hpp>
++#include <stout/try.hpp>
++
++TEST(Stout, Some)
++{
++ Option<int> o1 = Some(42);
++ EXPECT_SOME(o1);
++ EXPECT_EQ(42, o1.get());
++
++ Result<int> r1 = Some(42);
++ EXPECT_SOME(r1);
++ EXPECT_EQ(42, r1.get());
++
++ Try<Option<int> > t1 = Some(42);
++ ASSERT_SOME(t1);
++ EXPECT_SOME(t1.get());
++ EXPECT_EQ(42, t1.get().get());
++
++ Try<Result<int> > t2 = Some(42);
++ ASSERT_SOME(t2);
++ EXPECT_SOME(t2.get());
++ EXPECT_EQ(42, t2.get().get());
++
++ Option<Result<int> > o2 = Some(42);
++ ASSERT_SOME(o2);
++ EXPECT_SOME(o2.get());
++ EXPECT_EQ(42, o2.get().get());
++
++ Option<Result<int> > o3 = Some(Some(42));
++ ASSERT_SOME(o3);
++ EXPECT_SOME(o3.get());
++ EXPECT_EQ(42, o3.get().get());
++
++ Result<Option<int> > r2 = Some(42);
++ ASSERT_SOME(r2);
++ EXPECT_SOME(r2.get());
++ EXPECT_EQ(42, r2.get().get());
++
++ Result<Option<int> > r3 = Some(Some(42));
++ ASSERT_SOME(r3);
++ EXPECT_SOME(r3.get());
++ EXPECT_EQ(42, r3.get().get());
++
++ Option<std::string> o4 = Some("hello");
++ EXPECT_SOME(o4);
++ EXPECT_EQ("hello", o4.get());
++
++ Result<std::string> r4 = Some("world");
++ EXPECT_SOME(r4);
++ EXPECT_EQ("world", r4.get());
++
++ std::map<std::string, Option<std::string> > values;
++ values["no-debug"] = None();
++ values["debug"] = None();
++ values["debug"] = Some("true");
++ values["debug"] = Some("false");
++ values["name"] = Some("frank");
++}
+diff --git a/src/tests/stout/strings_tests.cpp b/src/tests/stout/strings_tests.cpp
+new file mode 100644
+index 0000000..b5a233f
+--- /dev/null
++++ b/src/tests/stout/strings_tests.cpp
+@@ -0,0 +1,298 @@
++#include <gtest/gtest.h>
++
++#include <gmock/gmock.h>
++
++#include <map>
++#include <string>
++#include <vector>
++
++#include <stout/format.hpp>
++#include <stout/gtest.hpp>
++#include <stout/strings.hpp>
++#include <stout/try.hpp>
++
++using std::map;
++using std::string;
++using std::vector;
++
++
++TEST(StringsTest, Format)
++{
++ Try<std::string> result = strings::format("%s %s", "hello", "world");
++ ASSERT_SOME(result);
++ EXPECT_EQ("hello world", result.get());
++
++ result = strings::format("hello %d", 42);
++ ASSERT_SOME(result);
++ EXPECT_EQ("hello 42", result.get());
++
++ result = strings::format("hello %s", "fourty-two");
++ ASSERT_SOME(result);
++ EXPECT_EQ("hello fourty-two", result.get());
++
++ string hello = "hello";
++
++ result = strings::format("%s %s", hello, "fourty-two");
++ ASSERT_SOME(result);
++ EXPECT_EQ("hello fourty-two", result.get());
++}
++
++
++TEST(StringsTest, Remove)
++{
++ EXPECT_EQ("heo word", strings::remove("hello world", "l"));
++ EXPECT_EQ("hel world", strings::remove("hello world", "lo"));
++ EXPECT_EQ("home/", strings::remove("/home/", "/", strings::PREFIX));
++ EXPECT_EQ("/home", strings::remove("/home/", "/", strings::SUFFIX));
++}
++
++
++TEST(StringsTest, Replace)
++{
++ EXPECT_EQ("hello*", strings::replace("hello/", "/", "*"));
++ EXPECT_EQ("*hello", strings::replace("/hello", "/", "*"));
++ EXPECT_EQ("*hello*world*", strings::replace("/hello/world/", "/", "*"));
++ EXPECT_EQ("*", strings::replace("/", "/", "*"));
++ EXPECT_EQ("hello world", strings::replace("hello world", "/", "*"));
++ EXPECT_EQ("***1***2***3***", strings::replace("/1/2/3/", "/", "***"));
++ EXPECT_EQ("123", strings::replace("/1/2/3/", "/", ""));
++ EXPECT_EQ("/1/2/3**", strings::replace("***1***2***3**", "***", "/"));
++ EXPECT_EQ("/1/2/3/", strings::replace("/1/2/3/", "", "*"));
++}
++
++
++TEST(StringsTest, Trim)
++{
++ EXPECT_EQ("", strings::trim("", " "));
++ EXPECT_EQ("", strings::trim(" ", " "));
++ EXPECT_EQ("hello world", strings::trim("hello world", " "));
++ EXPECT_EQ("hello world", strings::trim(" hello world", " "));
++ EXPECT_EQ("hello world", strings::trim("hello world ", " "));
++ EXPECT_EQ("hello world", strings::trim(" hello world ", " "));
++ EXPECT_EQ("hello world", strings::trim(" \t hello world\t ", " \t"));
++ EXPECT_EQ("hello world", strings::trim(" \t hello world\t \n\r "));
++}
++
++
++TEST(StringsTest, Tokenize)
++{
++ vector<string> tokens = strings::tokenize("hello world, what's up?", " ");
++ ASSERT_EQ(4u, tokens.size());
++ EXPECT_EQ("hello", tokens[0]);
++ EXPECT_EQ("world,", tokens[1]);
++ EXPECT_EQ("what's", tokens[2]);
++ EXPECT_EQ("up?", tokens[3]);
++}
++
++
++TEST(StringsTest, TokenizeStringWithDelimsAtStart)
++{
++ vector<string> tokens = strings::tokenize(" hello world, what's up?", " ");
++ ASSERT_EQ(4u, tokens.size());
++ EXPECT_EQ("hello", tokens[0]);
++ EXPECT_EQ("world,", tokens[1]);
++ EXPECT_EQ("what's", tokens[2]);
++ EXPECT_EQ("up?", tokens[3]);
++}
++
++
++TEST(StringsTest, TokenizeStringWithDelimsAtEnd)
++{
++ vector<string> tokens = strings::tokenize("hello world, what's up? ", " ");
++ ASSERT_EQ(4u, tokens.size());
++ EXPECT_EQ("hello", tokens[0]);
++ EXPECT_EQ("world,", tokens[1]);
++ EXPECT_EQ("what's", tokens[2]);
++ EXPECT_EQ("up?", tokens[3]);
++}
++
++
++TEST(StringsTest, TokenizeStringWithDelimsAtStartAndEnd)
++{
++ vector<string> tokens = strings::tokenize(" hello world, what's up? ", " ");
++ ASSERT_EQ(4u, tokens.size());
++ EXPECT_EQ("hello", tokens[0]);
++ EXPECT_EQ("world,", tokens[1]);
++ EXPECT_EQ("what's", tokens[2]);
++ EXPECT_EQ("up?", tokens[3]);
++}
++
++
++TEST(StringsTest, TokenizeWithMultipleDelims)
++{
++ vector<string> tokens = strings::tokenize("hello\tworld, \twhat's up?",
++ " \t");
++ ASSERT_EQ(4u, tokens.size());
++ EXPECT_EQ("hello", tokens[0]);
++ EXPECT_EQ("world,", tokens[1]);
++ EXPECT_EQ("what's", tokens[2]);
++ EXPECT_EQ("up?", tokens[3]);
++}
++
++
++TEST(StringsTest, TokenizeEmptyString)
++{
++ vector<string> tokens = strings::tokenize("", " ");
++ ASSERT_EQ(0u, tokens.size());
++}
++
++
++TEST(StringsTest, TokenizeDelimOnlyString)
++{
++ vector<string> tokens = strings::tokenize(" ", " ");
++ ASSERT_EQ(0u, tokens.size());
++}
++
++
++TEST(StringsTest, TokenizeNullByteDelim)
++{
++ string s;
++ s.push_back('\0');
++ s.push_back('\0');
++ s.push_back('\0');
++ s.push_back('h');
++ s.push_back('e');
++ s.push_back('l');
++ s.push_back('l');
++ s.push_back('o');
++ s.push_back('\0');
++ s.push_back('\0');
++ s.push_back('\0');
++ s.push_back('\0');
++ s.push_back('\0');
++ s.push_back('\0');
++ s.push_back('w');
++ s.push_back('o');
++ s.push_back('r');
++ s.push_back('l');
++ s.push_back('d');
++ s.push_back('\0');
++ s.push_back('\0');
++ s.push_back('\0');
++
++ vector<string> tokens = strings::tokenize(s, string(1, '\0'));
++
++ ASSERT_EQ(2u, tokens.size());
++ EXPECT_EQ("hello", tokens[0]);
++ EXPECT_EQ("world", tokens[1]);
++}
++
++
++TEST(StringsTest, SplitEmptyString)
++{
++ vector<string> tokens = strings::split("", ",");
++ ASSERT_EQ(1u, tokens.size());
++ EXPECT_EQ("", tokens[0]);
++}
++
++
++TEST(StringsTest, SplitDelimOnlyString)
++{
++ vector<string> tokens = strings::split(",,,", ",");
++ ASSERT_EQ(4u, tokens.size());
++ EXPECT_EQ("", tokens[0]);
++ EXPECT_EQ("", tokens[1]);
++ EXPECT_EQ("", tokens[2]);
++ EXPECT_EQ("", tokens[3]);
++}
++
++
++TEST(StringsTest, Split)
++{
++ vector<string> tokens = strings::split("foo,bar,,baz", ",");
++ ASSERT_EQ(4u, tokens.size());
++ EXPECT_EQ("foo", tokens[0]);
++ EXPECT_EQ("bar", tokens[1]);
++ EXPECT_EQ("", tokens[2]);
++ EXPECT_EQ("baz", tokens[3]);
++}
++
++
++TEST(StringsTest, SplitStringWithDelimsAtStart)
++{
++ vector<string> tokens = strings::split(",,foo,bar,,baz", ",");
++ ASSERT_EQ(6u, tokens.size());
++ EXPECT_EQ("", tokens[0]);
++ EXPECT_EQ("", tokens[1]);
++ EXPECT_EQ("foo", tokens[2]);
++ EXPECT_EQ("bar", tokens[3]);
++ EXPECT_EQ("", tokens[4]);
++ EXPECT_EQ("baz", tokens[5]);
++}
++
++
++TEST(StringsTest, SplitStringWithDelimsAtEnd)
++{
++ vector<string> tokens = strings::split("foo,bar,,baz,,", ",");
++ ASSERT_EQ(6u, tokens.size());
++ EXPECT_EQ("foo", tokens[0]);
++ EXPECT_EQ("bar", tokens[1]);
++ EXPECT_EQ("", tokens[2]);
++ EXPECT_EQ("baz", tokens[3]);
++ EXPECT_EQ("", tokens[4]);
++ EXPECT_EQ("", tokens[5]);
++}
++
++
++TEST(StringsTest, SplitStringWithDelimsAtStartAndEnd)
++{
++ vector<string> tokens = strings::split(",,foo,bar,,", ",");
++ ASSERT_EQ(6u, tokens.size());
++ EXPECT_EQ("", tokens[0]);
++ EXPECT_EQ("", tokens[1]);
++ EXPECT_EQ("foo", tokens[2]);
++ EXPECT_EQ("bar", tokens[3]);
++ EXPECT_EQ("", tokens[4]);
++ EXPECT_EQ("", tokens[5]);
++}
++
++
++TEST(StringsTest, SplitWithMultipleDelims)
++{
++ vector<string> tokens = strings::split("foo.bar,.,.baz.", ",.");
++ ASSERT_EQ(7u, tokens.size());
++ EXPECT_EQ("foo", tokens[0]);
++ EXPECT_EQ("bar", tokens[1]);
++ EXPECT_EQ("", tokens[2]);
++ EXPECT_EQ("", tokens[3]);
++ EXPECT_EQ("", tokens[4]);
++ EXPECT_EQ("baz", tokens[5]);
++ EXPECT_EQ("", tokens[6]);
++}
++
++
++TEST(StringsTest, Pairs)
++{
++ map<string, vector<string> > pairs = strings::pairs("one=1,two=2", ",", "=");
++ ASSERT_EQ(2u, pairs.size());
++ ASSERT_EQ(1u, pairs.count("one"));
++ ASSERT_EQ(1u, pairs["one"].size());
++ EXPECT_EQ("1", pairs["one"].front());
++ ASSERT_EQ(1u, pairs.count("two"));
++ ASSERT_EQ(1u, pairs["two"].size());
++ EXPECT_EQ("2", pairs["two"].front());
++
++ pairs = strings::pairs("foo=1;bar=2;baz;foo=3;bam=1=2", ";&", "=");
++ ASSERT_EQ(2, pairs.size());
++ ASSERT_EQ(1u, pairs.count("foo"));
++ ASSERT_EQ(2u, pairs["foo"].size());
++ ASSERT_EQ("1", pairs["foo"].front());
++ ASSERT_EQ("3", pairs["foo"].back());
++ ASSERT_EQ(1u, pairs.count("bar"));
++ ASSERT_EQ("2", pairs["bar"].front());
++}
++
++
++TEST(StringsTest, StartsWith)
++{
++ EXPECT_TRUE(strings::startsWith("hello world", "hello"));
++ EXPECT_FALSE(strings::startsWith("hello world", "no"));
++ EXPECT_FALSE(strings::startsWith("hello world", "ello"));
++}
++
++
++TEST(StringsTest, Contains)
++{
++ EXPECT_TRUE(strings::contains("hello world", "world"));
++ EXPECT_FALSE(strings::contains("hello world", "no"));
++}
+diff --git a/src/tests/stout/thread_tests.cpp b/src/tests/stout/thread_tests.cpp
+new file mode 100644
+index 0000000..7519b12
+--- /dev/null
++++ b/src/tests/stout/thread_tests.cpp
+@@ -0,0 +1,26 @@
++#include <gtest/gtest.h>
++
++#include <string>
++
++#include <stout/thread.hpp>
++
++TEST(Thread, local)
++{
++ ThreadLocal<std::string>* _s_ = new ThreadLocal<std::string>();
++
++ std::string* s = new std::string();
++
++ ASSERT_TRUE(*(_s_) == NULL);
++
++ (*_s_) = s;
++
++ ASSERT_TRUE(*(_s_) == s);
++ ASSERT_FALSE(*(_s_) == NULL);
++
++ (*_s_) = NULL;
++
++ ASSERT_TRUE(*(_s_) == NULL);
++
++ delete s;
++ delete _s_;
++}
+diff --git a/src/tests/stout/uuid_tests.cpp b/src/tests/stout/uuid_tests.cpp
+new file mode 100644
+index 0000000..ad1d986
+--- /dev/null
++++ b/src/tests/stout/uuid_tests.cpp
+@@ -0,0 +1,37 @@
++#include <gtest/gtest.h>
++
++#include <gmock/gmock.h>
++
++#include <string>
++
++#include <stout/uuid.hpp>
++
++using std::string;
++
++
++TEST(UUIDTest, test)
++{
++ UUID uuid1 = UUID::random();
++ UUID uuid2 = UUID::fromBytes(uuid1.toBytes());
++ UUID uuid3 = uuid2;
++
++ EXPECT_EQ(uuid1, uuid2);
++ EXPECT_EQ(uuid2, uuid3);
++ EXPECT_EQ(uuid1, uuid3);
++
++ string bytes1 = uuid1.toBytes();
++ string bytes2 = uuid2.toBytes();
++ string bytes3 = uuid3.toBytes();
++
++ EXPECT_EQ(bytes1, bytes2);
++ EXPECT_EQ(bytes2, bytes3);
++ EXPECT_EQ(bytes1, bytes3);
++
++ string string1 = uuid1.toString();
++ string string2 = uuid2.toString();
++ string string3 = uuid3.toString();
++
++ EXPECT_EQ(string1, string2);
++ EXPECT_EQ(string2, string3);
++ EXPECT_EQ(string1, string3);
++}
diff --git a/mesos-master.service b/mesos-master.service
new file mode 100644
index 0000000..c20e777
--- /dev/null
+++ b/mesos-master.service
@@ -0,0 +1,22 @@
+
+[Unit]
+Description=Mesos Cluster Manager
+After=network.target
+Wants=network.target
+
+[Service]
+EnvironmentFile=-/etc/sysconfig/mesos/mesos-master-env
+#Type=forking
+ExecStart=/usr/sbin/mesos-master --log_dir=/var/log/mesos --port=5050
+ExecStop=/usr/bin/killall -s 9 mesos-master
+ExecReload=/bin/kill -HUP $MAINPID
+User=mesos
+Group=mesos
+Restart=always
+RestartSec=20
+LimitNOFILE=16384
+#ControlGroup=cpu:/mesos
+
+[Install]
+WantedBy=multi-user.target
+
diff --git a/mesos-slave.service b/mesos-slave.service
new file mode 100644
index 0000000..4b9de67
--- /dev/null
+++ b/mesos-slave.service
@@ -0,0 +1,22 @@
+
+[Unit]
+Description=Mesos Cluster Manager
+After=network.target
+Wants=network.target
+
+[Service]
+EnvironmentFile=-/etc/sysconfig/mesos/mesos-slave-env
+#Type=forking
+ExecStart=/usr/sbin/mesos-slave --master=localhost:5050 --log_dir=/var/log/mesos --work_dir=/var/run/mesos
+ExecStop=/usr/bin/killall -s 9 mesos-slave
+ExecReload=/bin/kill -HUP $MAINPID
+User=mesos
+Group=mesos
+Restart=always
+RestartSec=20
+LimitNOFILE=16384
+#ControlGroup=cpu:/mesos
+
+[Install]
+WantedBy=multi-user.target
+
diff --git a/mesos-tmpfiles.conf b/mesos-tmpfiles.conf
new file mode 100644
index 0000000..2d444e0
--- /dev/null
+++ b/mesos-tmpfiles.conf
@@ -0,0 +1 @@
+d /var/run/mesos 0775 mesos mesos -
diff --git a/mesos.spec b/mesos.spec
new file mode 100644
index 0000000..07ccb21
--- /dev/null
+++ b/mesos.spec
@@ -0,0 +1,239 @@
+%global commit afe994774266154c544f5efc37f31a74cbf8a200
+
+%global shortcommit %(c=%{commit}; echo ${c:0:7})
+%global gentag 0.16.0-rc3
+
+%global skiptests 1
+
+Name: mesos
+Version: 0.16.0
+Release: 3.%{shortcommit}%{?dist}
+Summary: Cluster manager for sharing distributed application frameworks
+License: ASL 2.0
+URL: http://mesos.apache.org/
+
+Source0: https://github.com/apache/mesos/archive/%{commit}/%{name}-%{version}-%{shortcommit}.tar.gz
+Source1: %{name}-tmpfiles.conf
+Source2: %{name}-master.service
+Source3: %{name}-slave.service
+
+#####################################
+# NOTE: This patch has been accepted upstream and can be removed next release
+#####################################
+Patch0: https://issues.apache.org/jira/secure/attachment/12615152/MESOS-831.patch
+
+#####################################
+# NOTE: The modifications have been broken into three patches which are consistent
+# with *many* other projects, and are tracking @
+#
+# https://github.com/timothysc/mesos
+# Full integration stream is: https://github.com/timothysc/mesos/tree/0.16.0-integ
+#
+# The shuffle patch is maintained because it is a
+# patch that is trying to be pushed upstream, thus breaking it out as a series
+# of steps doesn't make sense, but has been isolated into it's own patch per review.
+####################################
+#git diff --no-ext-diff 0.16.0 0.16.0-pre-shuffle > build_mods.patch
+Patch1: build_mods.patch
+# git diff --no-ext-diff 0.16.0-pre-shuffle 0.16.0-post-shuffle > fileshuffle_mods.patch
+# b/c order matters on a shuffle-patch.
+Patch2: fileshuffle_mods.patch
+# git diff --no-ext-diff 0.16.0 0.16.0-testing >testing_mods.patch
+Patch3: testing_mods.patch
+
+BuildRequires: libtool
+BuildRequires: automake
+BuildRequires: autoconf
+BuildRequires: zlib-devel
+BuildRequires: libcurl-devel
+BuildRequires: http-parser-devel
+BuildRequires: boost-devel
+BuildRequires: glog-devel
+BuildRequires: gmock-devel
+BuildRequires: gtest-devel
+BuildRequires: gperftools-devel
+BuildRequires: libev-devel
+BuildRequires: leveldb-devel
+BuildRequires: protobuf-devel
+BuildRequires: python-boto
+BuildRequires: python-setuptools
+BuildRequires: protobuf-python
+BuildRequires: protobuf-java
+BuildRequires: python2-devel
+BuildRequires: zookeeper-lib-devel
+BuildRequires: openssl-devel
+BuildRequires: cyrus-sasl-devel
+BuildRequires: java-devel
+BuildRequires: systemd
+
+Requires: protobuf-python
+
+######################################
+# NOTE: arm has no planned support upstream
+# and fails to compile, thus disabled
+######################################
+ExcludeArch: %{arm}
+
+%description
+Apache Mesos is a cluster manager that provides efficient resource
+isolation and sharing across distributed applications, or frameworks.
+It can run Hadoop, MPI, Hypertable, Spark, and other applications on
+a dynamically shared pool of nodes.
+
+##############################################
+%package devel
+Summary: Header files for Mesos development
+Group: Development/Libraries
+Requires: %{name}%{?_isa} = %{version}-%{release}
+
+%description devel
+Provides header and development files for %{name}.
+##############################################
+
+%prep
+%setup -q -n %{name}-%{commit}
+
+%patch0 -p1
+%patch1 -p1
+%patch2 -p1
+%patch3 -p1
+
+######################################
+# NOTE: remove all bundled elements
+# Still pushing upstream on removal
+# but it may take some time.
+######################################
+rm -rf 3rdparty
+
+%build
+autoreconf -vfi
+%configure --disable-static
+make
+######################################
+# NOTE: %{?_smp_mflags}
+# currently fails upstream
+######################################
+
+######################################
+# NOTE: https://issues.apache.org/jira/browse/MESOS-899
+# Python installation is still TBD:
+#
+# export PYTHONPATH=${PYTHONPATH}:{buildroot}{python_sitearch}
+# mkdir -p {buildroot}{python_sitearch}
+# python src/python/setup.py install --prefix={buildroot}{python_sitearch}
+######################################
+
+%check
+######################################
+# NOTE: as of 0.16.0 &> there has been a change in the startup routines which cause
+# a substantial number of tests to fail/hang under mock. However, they run fine under a local environment
+# so they are disabled by default at this time.
+######################################
+%if %skiptests
+ echo "Skipping tests, do to mock issues"
+%else
+ export LD_LIBRARY_PATH=`pwd`/src/.libs
+ make check
+%endif
+
+%install
+%make_install
+
+# fedora guidelines no .a|.la
+rm -f %{buildroot}%{_libdir}/*.la
+
+# system integration sysconfig setting
+mv %{buildroot}%{_sysconfdir}/%{name}/deploy/* %{buildroot}%{_sysconfdir}/%{name}
+rm -rf mv %{buildroot}%{_sysconfdir}/%{name}/deploy
+
+mkdir -p %{buildroot}%{_sysconfdir}/tmpfiles.d
+install -m 0644 %{SOURCE1} %{buildroot}%{_sysconfdir}/tmpfiles.d/%{name}.conf
+
+mkdir -p -m0755 %{buildroot}/%{_var}/log/%{name}
+mkdir -p %{buildroot}%{_unitdir}
+install -m 0644 %{SOURCE2} %{SOURCE3} %{buildroot}%{_unitdir}/
+
+mkdir -p %{buildroot}%{python_sitelib}
+mv %{buildroot}%{_libexecdir}/%{name}/python/%{name} %{buildroot}%{python_sitelib}
+rm -rf %{buildroot}%{_libexecdir}/%{name}/python
+
+############################################
+%files
+%doc LICENSE README.md
+%{_libdir}/libmesos-%{version}.so.*
+%{_bindir}/mesos*
+%{_sbindir}/mesos-*
+%{_datadir}/%{name}/
+%{_libexecdir}/%{name}/
+#system integration aspects
+%{_sysconfdir}/%{name}/
+%{python_sitelib}/%{name}/
+%{_var}/log/%{name}/
+%config(noreplace) %_sysconfdir/tmpfiles.d/%{name}.conf
+%{_unitdir}/%{name}*.service
+
+%files devel
+%doc LICENSE README.md
+%{_includedir}/mesos/
+%{_libdir}/libmesos.so
+%{_libdir}/pkgconfig/%{name}.pc
+############################################
+
+%pre
+getent group mesos >/dev/null || groupadd -f -r mesos
+if ! getent passwd mesos >/dev/null ; then
+ useradd -r -g mesos -d %{_sharedstatedir}/%{name} -s /sbin/nologin \
+ -c "%{name} daemon account" mesos
+fi
+exit 0
+
+%post
+%systemd_post %{name}-slave.service %{name}-master.service
+/sbin/ldconfig
+
+%preun
+%systemd_preun %{name}-slave.service %{name}-master.service
+
+%postun
+%systemd_postun_with_restart %{name}-slave.service %{name}-master.service
+/sbin/ldconfig
+
+%changelog
+* Mon Jan 20 2014 Timothy St. Clair <tstclair at redhat.com> - 0.16.0-3.afe9947
+- Updated to 0.16.0-rc3
+
+* Mon Jan 13 2014 Timothy St. Clair <tstclair at redhat.com> - 0.16.0-2.d0cb03f
+- Updating per review
+
+* Tue Nov 19 2013 Timothy St. Clair <tstclair at redhat.com> - 0.16.0-1.d3557e8
+- Update to latest upstream tip.
+
+* Thu Oct 31 2013 Timothy St. Clair <tstclair at redhat.com> - 0.15.0-4.42f8640
+- Merge in latest upstream developments
+
+* Fri Oct 18 2013 Timothy St. Clair <tstclair at redhat.com> - 0.15.0-4.464661f
+- Package restructuring for subsuming library dependencies dependencies.
+
+* Thu Oct 3 2013 Timothy St. Clair <tstclair at redhat.com> - 0.15.0-3.8037f97
+- Cleaning package for review
+
+* Fri Sep 20 2013 Timothy St. Clair <tstclair at redhat.com> - 0.15.0-0.2.01ccdb
+- Cleanup for system integration
+
+* Tue Sep 17 2013 Timothy St. Clair <tstclair at redhat.com> - 0.15.0-0.1.1bc2941
+- Update to the latest mesos HEAD
+
+* Wed Aug 14 2013 Igor Gnatenko <i.gnatenko.brain at gmail.com> - 0.12.1-0.4.dff92ff
+- spec: cleanups and fixes
+- spec: fix systemd daemon
+
+* Mon Aug 12 2013 Timothy St. Clair <tstclair at redhat.com> - 0.12.1-0.3.dff92ff
+- Update and add install targets.
+
+* Fri Aug 9 2013 Igor Gnatenko <i.gnatenko.brain at gmail.com> - 0.12.1-0.2.cba04c1
+- Update to latest
+- Add python-boto as BR
+- other fixes
+
+* Thu Aug 1 2013 Igor Gnatenko <i.gnatenko.brain at gmail.com> - 0.12.1-0.1.eb17018
+- Initial release
diff --git a/sources b/sources
index e69de29..d2d0d7f 100644
--- a/sources
+++ b/sources
@@ -0,0 +1 @@
+14aa1a1b51bb1aa66888fb98974dd950 mesos-0.16.0-afe9947.tar.gz
diff --git a/testing_mods.patch b/testing_mods.patch
new file mode 100644
index 0000000..e3aeb65
--- /dev/null
+++ b/testing_mods.patch
@@ -0,0 +1,277 @@
+diff --git a/src/tests/allocator_tests.cpp b/src/tests/allocator_tests.cpp
+index 3f231e3..c0ebb22 100644
+--- a/src/tests/allocator_tests.cpp
++++ b/src/tests/allocator_tests.cpp
+@@ -944,7 +944,7 @@ TYPED_TEST(AllocatorTest, OutOfOrderDispatch)
+ // Checks that if a framework launches a task and then fails over to a
+ // new scheduler, the task's resources are not reoffered as long as it
+ // is running.
+-TYPED_TEST(AllocatorTest, SchedulerFailover)
++TYPED_TEST(AllocatorTest, DISABLED_SchedulerFailover)
+ {
+ EXPECT_CALL(this->allocator, initialize(_, _, _));
+
+diff --git a/src/tests/examples_tests.cpp b/src/tests/examples_tests.cpp
+index 28ff0f3..801c134 100644
+--- a/src/tests/examples_tests.cpp
++++ b/src/tests/examples_tests.cpp
+@@ -23,13 +23,13 @@
+
+ // Run each of the sample frameworks in local mode.
+ TEST_SCRIPT(ExamplesTest, TestFramework, "test_framework_test.sh")
+-TEST_SCRIPT(ExamplesTest, NoExecutorFramework, "no_executor_framework_test.sh")
++TEST_SCRIPT(ExamplesTest, DISABLED_NoExecutorFramework, "no_executor_framework_test.sh")
+
+ #ifdef MESOS_HAS_JAVA
+-TEST_SCRIPT(ExamplesTest, JavaFramework, "java_framework_test.sh")
+-TEST_SCRIPT(ExamplesTest, JavaException, "java_exception_test.sh")
++TEST_SCRIPT(ExamplesTest, DISABLED_JavaFramework, "java_framework_test.sh")
++TEST_SCRIPT(ExamplesTest, DISABLED_JavaException, "java_exception_test.sh")
+ #endif
+
+ #ifdef MESOS_HAS_PYTHON
+-TEST_SCRIPT(ExamplesTest, PythonFramework, "python_framework_test.sh")
++TEST_SCRIPT(ExamplesTest, DISABLED_PythonFramework, "python_framework_test.sh")
+ #endif
+diff --git a/src/tests/group_tests.cpp b/src/tests/group_tests.cpp
+index 957256e..cc53c86 100644
+--- a/src/tests/group_tests.cpp
++++ b/src/tests/group_tests.cpp
+@@ -16,7 +16,7 @@
+ * limitations under the License.
+ */
+
+-#include <zookeeper.h>
++#include <zookeeper/zookeeper.h>
+
+ #include <gmock/gmock.h>
+
+@@ -80,7 +80,7 @@ TEST_F(GroupTest, Group)
+ }
+
+
+-TEST_F(GroupTest, GroupJoinWithDisconnect)
++TEST_F(GroupTest, DISABLED_GroupJoinWithDisconnect)
+ {
+ Group group(server->connectString(), NO_TIMEOUT, "/test/");
+
+@@ -102,7 +102,7 @@ TEST_F(GroupTest, GroupJoinWithDisconnect)
+ }
+
+
+-TEST_F(GroupTest, GroupDataWithDisconnect)
++TEST_F(GroupTest, DISABLED_GroupDataWithDisconnect)
+ {
+ Group group(server->connectString(), NO_TIMEOUT, "/test/");
+
+@@ -128,7 +128,7 @@ TEST_F(GroupTest, GroupDataWithDisconnect)
+ }
+
+
+-TEST_F(GroupTest, GroupCancelWithDisconnect)
++TEST_F(GroupTest, DISABLED_GroupCancelWithDisconnect)
+ {
+ Group group(server->connectString(), NO_TIMEOUT, "/test/");
+
+diff --git a/src/tests/isolator_tests.cpp b/src/tests/isolator_tests.cpp
+index 45a41ca..d742b6d 100644
+--- a/src/tests/isolator_tests.cpp
++++ b/src/tests/isolator_tests.cpp
+@@ -74,7 +74,7 @@ typedef ::testing::Types<ProcessIsolator> IsolatorTypes;
+
+ TYPED_TEST_CASE(IsolatorTest, IsolatorTypes);
+
+-TYPED_TEST(IsolatorTest, Usage)
++TYPED_TEST(IsolatorTest, DISABLED_Usage)
+ {
+ Try<PID<Master> > master = this->StartMaster();
+ ASSERT_SOME(master);
+diff --git a/src/tests/log_tests.cpp b/src/tests/log_tests.cpp
+index ff5f86c..c8c1ec0 100644
+--- a/src/tests/log_tests.cpp
++++ b/src/tests/log_tests.cpp
+@@ -170,7 +170,7 @@ TEST_F(ReplicaTest, Append)
+ }
+
+
+-TEST_F(ReplicaTest, Recover)
++TEST_F(ReplicaTest, DISABLED_Recover)
+ {
+ const std::string path = os::getcwd() + "/.log";
+
+diff --git a/src/tests/master_contender_detector_tests.cpp b/src/tests/master_contender_detector_tests.cpp
+index 76464ea..3cb2e39 100644
+--- a/src/tests/master_contender_detector_tests.cpp
++++ b/src/tests/master_contender_detector_tests.cpp
+@@ -16,7 +16,7 @@
+ * limitations under the License.
+ */
+
+-#include <zookeeper.h>
++#include <zookeeper/zookeeper.h>
+
+ #include <gmock/gmock.h>
+
+@@ -269,7 +269,7 @@ TEST_F(ZooKeeperMasterContenderDetectorTest, MasterContenders)
+
+ // Master contention and detection fail when the network is down, it
+ // recovers when the network is back up.
+-TEST_F(ZooKeeperMasterContenderDetectorTest, ContenderDetectorShutdownNetwork)
++TEST_F(ZooKeeperMasterContenderDetectorTest, DISABLED_ContenderDetectorShutdownNetwork)
+ {
+ Clock::pause();
+
+@@ -337,7 +337,7 @@ TEST_F(ZooKeeperMasterContenderDetectorTest, ContenderDetectorShutdownNetwork)
+ // ZooKeeper session timeout. This is to enforce that we manually
+ // expire the session when we do not get reconnected within the
+ // timeout.
+-TEST_F(ZooKeeperMasterContenderDetectorTest, MasterDetectorTimedoutSession)
++TEST_F(ZooKeeperMasterContenderDetectorTest, DISABLED_MasterDetectorTimedoutSession)
+ {
+ // Use an arbitrary timeout value.
+ Duration sessionTimeout(Seconds(5));
+diff --git a/src/tests/reaper_tests.cpp b/src/tests/reaper_tests.cpp
+index 608ec0e..03292a0 100644
+--- a/src/tests/reaper_tests.cpp
++++ b/src/tests/reaper_tests.cpp
+@@ -104,7 +104,7 @@ TEST(ReaperTest, NonChildProcess)
+
+ // This test checks that the Reaper can monitor a child process with
+ // accurate exit status returned.
+-TEST(ReaperTest, ChildProcess)
++TEST(ReaperTest, DISABLED_ChildProcess)
+ {
+ ASSERT_TRUE(GTEST_IS_THREADSAFE);
+
+diff --git a/src/tests/slave_recovery_tests.cpp b/src/tests/slave_recovery_tests.cpp
+index 250083d..50cf1bf 100644
+--- a/src/tests/slave_recovery_tests.cpp
++++ b/src/tests/slave_recovery_tests.cpp
+@@ -610,7 +610,7 @@ TYPED_TEST(SlaveRecoveryTest, RecoverUnregisteredExecutor)
+ // The command executor terminates when the slave is down.
+ // When it comes back up with recovery=reconnect, make
+ // sure the task is properly transitioned to FAILED.
+-TYPED_TEST(SlaveRecoveryTest, RecoverTerminatedExecutor)
++TYPED_TEST(SlaveRecoveryTest, DISABLED_RecoverTerminatedExecutor)
+ {
+ Try<PID<Master> > master = this->StartMaster();
+ ASSERT_SOME(master);
+@@ -1224,7 +1224,7 @@ TYPED_TEST(SlaveRecoveryTest, NonCheckpointingSlave)
+ // running before the slave restarted. This test ensures that a
+ // restarted slave is able to communicate with all components
+ // (scheduler, master, executor).
+-TYPED_TEST(SlaveRecoveryTest, KillTask)
++TYPED_TEST(SlaveRecoveryTest, DISABLED_KillTask)
+ {
+ Try<PID<Master> > master = this->StartMaster();
+ ASSERT_SOME(master);
+@@ -1689,7 +1689,7 @@ TYPED_TEST(SlaveRecoveryTest, RegisterDisconnectedSlave)
+ // This test verifies that a KillTask message received by the
+ // master when a checkpointing slave is disconnected is properly
+ // reconciled when the slave reregisters.
+-TYPED_TEST(SlaveRecoveryTest, ReconcileKillTask)
++TYPED_TEST(SlaveRecoveryTest, DISABLED_ReconcileKillTask)
+ {
+ Try<PID<Master> > master = this->StartMaster();
+ ASSERT_SOME(master);
+@@ -2026,7 +2026,7 @@ TYPED_TEST(SlaveRecoveryTest, ReconcileTasksMissingFromSlave)
+ // running before the slave restarted. A scheduler failover happens
+ // when the slave is down. This test verifies that a scheduler
+ // failover will not affect the slave recovery process.
+-TYPED_TEST(SlaveRecoveryTest, SchedulerFailover)
++TYPED_TEST(SlaveRecoveryTest, DISABLED_SchedulerFailover)
+ {
+ Try<PID<Master> > master = this->StartMaster();
+ ASSERT_SOME(master);
+@@ -2303,7 +2303,7 @@ TYPED_TEST(SlaveRecoveryTest, PartitionedSlave)
+ // This test verifies that if the master changes when the slave is
+ // down, the slave can still recover the task when it restarts. We
+ // verify its correctness by killing the task from the scheduler.
+-TYPED_TEST(SlaveRecoveryTest, MasterFailover)
++TYPED_TEST(SlaveRecoveryTest, DISABLED_MasterFailover)
+ {
+ // Step 1. Run a task.
+ Try<PID<Master> > master = this->StartMaster();
+@@ -2441,7 +2441,7 @@ TYPED_TEST(SlaveRecoveryTest, MasterFailover)
+ // framework launches a task before the slave goes down. We verify
+ // that the two frameworks and their tasks are recovered after the
+ // slave restarts.
+-TYPED_TEST(SlaveRecoveryTest, MultipleFrameworks)
++TYPED_TEST(SlaveRecoveryTest, DISABLED_MultipleFrameworks)
+ {
+ Try<PID<Master> > master = this->StartMaster();
+ ASSERT_SOME(master);
+@@ -2634,7 +2634,7 @@ TYPED_TEST_CASE(SlaveRecoveryProcessIsolatorTest,
+
+ // This test verifies that slave recovery works properly even if
+ // multiple slaves are co-located on the same host.
+-TYPED_TEST(SlaveRecoveryProcessIsolatorTest, MultipleSlaves)
++TYPED_TEST(SlaveRecoveryProcessIsolatorTest, DISABLED_MultipleSlaves)
+ {
+ Try<PID<Master> > master = this->StartMaster();
+ ASSERT_SOME(master);
+diff --git a/src/tests/zookeeper.cpp b/src/tests/zookeeper.cpp
+index 8bb4901..fe99012 100644
+--- a/src/tests/zookeeper.cpp
++++ b/src/tests/zookeeper.cpp
+@@ -50,12 +50,12 @@ const Duration ZooKeeperTest::NO_TIMEOUT = Milliseconds(5000);
+ void ZooKeeperTest::SetUpTestCase()
+ {
+ if (!Jvm::created()) {
+- std::string zkHome = flags.build_dir +
+- "/3rdparty/zookeeper-" ZOOKEEPER_VERSION;
++
++ // TODO (tstclair): replace with auto-found --with params
++ std::string jarHome = "/usr/share/java/";
+
+ std::string classpath = "-Djava.class.path=" +
+- zkHome + "/zookeeper-" ZOOKEEPER_VERSION ".jar:" +
+- zkHome + "/lib/log4j-1.2.15.jar";
++ jarHome + "zookeeper/zookeeper.jar:" + jarHome + "slf4j/api.jar:" + jarHome + "slf4j/log4j12.jar:" + jarHome + "log4j.jar";
+
+ LOG(INFO) << "Using classpath setup: " << classpath << std::endl;
+
+diff --git a/src/tests/zookeeper_test_server.cpp b/src/tests/zookeeper_test_server.cpp
+index dc53d6a..1ee0607 100644
+--- a/src/tests/zookeeper_test_server.cpp
++++ b/src/tests/zookeeper_test_server.cpp
+@@ -86,7 +86,7 @@ std::string ZooKeeperTestServer::connectString() const
+
+ void ZooKeeperTestServer::shutdownNetwork()
+ {
+- if (started && connectionFactory && connectionFactory->isAlive()) {
++ if (started /*&& connectionFactory->isAlive()*/) {
+ connectionFactory->shutdown();
+ delete connectionFactory;
+ connectionFactory = NULL;
+diff --git a/src/tests/zookeeper_tests.cpp b/src/tests/zookeeper_tests.cpp
+index a5fe9e1..12857ef 100644
+--- a/src/tests/zookeeper_tests.cpp
++++ b/src/tests/zookeeper_tests.cpp
+@@ -16,7 +16,7 @@
+ * limitations under the License.
+ */
+
+-#include <zookeeper.h>
++#include <zookeeper/zookeeper.h>
+
+ #include <gmock/gmock.h>
+
+@@ -170,7 +170,7 @@ TEST_F(ZooKeeperTest, LeaderDetector)
+ }
+
+
+-TEST_F(ZooKeeperTest, LeaderDetectorFailureHandling)
++TEST_F(ZooKeeperTest, DISABLED_LeaderDetectorFailureHandling)
+ {
+ Seconds timeout(10);
+ Group group(server->connectString(), timeout, "/test/");
+@@ -233,7 +233,7 @@ TEST_F(ZooKeeperTest, LeaderDetectorFailureHandling)
+ }
+
+
+-TEST_F(ZooKeeperTest, LeaderContender)
++TEST_F(ZooKeeperTest, DISABLED_LeaderContender)
+ {
+ Seconds timeout(10);
+ Group group(server->connectString(), timeout, "/test/");
More information about the scm-commits
mailing list