You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by an...@apache.org on 2018/06/28 18:22:15 UTC

[01/16] mesos git commit: Windows: Ported `docker_containerizer_tests.cpp`.

Repository: mesos
Updated Branches:
  refs/heads/master f6f35a88b -> 7c9c72dad


Windows: Ported `docker_containerizer_tests.cpp`.

With some Docker bug fixes and the IOCP backend, the remaining Docker
tests have been ported. The only remaining Docker tests that aren't
ported are either due to limitations on Windows Containers or
unimplemented features (e.g. persistent volume and hooks).

Review: https://reviews.apache.org/r/67457/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/7c9c72da
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/7c9c72da
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/7c9c72da

Branch: refs/heads/master
Commit: 7c9c72dad2b343b2008b2d53bffbe6d0dd032b80
Parents: 1f4ead9
Author: Akash Gupta <ak...@hotmail.com>
Authored: Wed Jun 27 14:30:19 2018 -0700
Committer: Andrew Schwartzmeyer <an...@schwartzmeyer.com>
Committed: Wed Jun 27 15:06:10 2018 -0700

----------------------------------------------------------------------
 src/tests/CMakeLists.txt                        |  10 +-
 .../docker_containerizer_tests.cpp              | 316 +++++++++++++++----
 2 files changed, 257 insertions(+), 69 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/7c9c72da/src/tests/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index b9c906d..695b6f5 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -157,8 +157,13 @@ list(APPEND MESOS_TESTS_SRC
 list(APPEND MESOS_TESTS_SRC
   containerizer/containerizer_tests.cpp
   containerizer/cpu_isolator_tests.cpp
-  containerizer/memory_isolator_tests.cpp
-  containerizer/docker_tests.cpp)
+  containerizer/docker_tests.cpp
+  containerizer/memory_isolator_tests.cpp)
+
+if (NOT WIN32 OR ENABLE_LIBWINIO)
+  list(APPEND MESOS_TESTS_SRC
+    containerizer/docker_containerizer_tests.cpp)
+endif ()
 
 if (NOT WIN32)
   list(APPEND MESOS_TESTS_SRC
@@ -197,7 +202,6 @@ if (NOT WIN32)
   list(APPEND MESOS_TESTS_SRC
     containerizer/appc_spec_tests.cpp
     containerizer/composing_containerizer_tests.cpp
-    containerizer/docker_containerizer_tests.cpp
     containerizer/docker_spec_tests.cpp
     containerizer/environment_secret_isolator_tests.cpp
     containerizer/io_switchboard_tests.cpp

http://git-wip-us.apache.org/repos/asf/mesos/blob/7c9c72da/src/tests/containerizer/docker_containerizer_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/containerizer/docker_containerizer_tests.cpp b/src/tests/containerizer/docker_containerizer_tests.cpp
index 194308b..43105e5 100644
--- a/src/tests/containerizer/docker_containerizer_tests.cpp
+++ b/src/tests/containerizer/docker_containerizer_tests.cpp
@@ -20,9 +20,10 @@
 
 #include <mesos/slave/container_logger.hpp>
 
-#include <process/io.hpp>
+#include <process/collect.hpp>
 #include <process/future.hpp>
 #include <process/gmock.hpp>
+#include <process/io.hpp>
 #include <process/owned.hpp>
 #include <process/subprocess.hpp>
 
@@ -97,6 +98,14 @@ namespace mesos {
 namespace internal {
 namespace tests {
 
+#ifdef __WINDOWS__
+static constexpr char DOCKER_INKY_IMAGE[] = "akagup/inky";
+static constexpr char DOCKER_LIST_COMMAND[] = "dir";
+#else
+static constexpr char DOCKER_INKY_IMAGE[] = "mesosphere/inky";
+static constexpr char DOCKER_LIST_COMMAND[] = "ls";
+#endif // __WINDOWS__
+
 
 static
 ContainerInfo createDockerInfo(const string& imageName)
@@ -127,15 +136,26 @@ public:
   static bool exists(
       const process::Shared<Docker>& docker,
       const ContainerID& containerId,
-      ContainerState state = ContainerState::EXISTS)
+      ContainerState state = ContainerState::EXISTS,
+      bool retry = true)
   {
     Duration waited = Duration::zero();
     string expectedName = containerName(containerId);
 
+#ifdef __WINDOWS__
+    constexpr Duration waitInspect = Seconds(10);
+    constexpr Duration waitInterval = Milliseconds(500);
+    constexpr Duration waitMax = Seconds(15);
+#else
+    constexpr Duration waitInspect = Seconds(3);
+    constexpr Duration waitInterval = Milliseconds(200);
+    constexpr Duration waitMax = Seconds(5);
+#endif // __WINDOWS__
+
     do {
       Future<Docker::Container> inspect = docker->inspect(expectedName);
 
-      if (!inspect.await(Seconds(3))) {
+      if (!inspect.await(waitInspect)) {
         return false;
       }
 
@@ -152,9 +172,9 @@ public:
         }
       }
 
-      os::sleep(Milliseconds(200));
-      waited += Milliseconds(200);
-    } while (waited < Seconds(5));
+      os::sleep(waitInterval);
+      waited += waitInterval;
+    } while (retry && waited < waitMax);
 
     return false;
   }
@@ -172,6 +192,25 @@ public:
     return false;
   }
 
+  virtual void SetUp()
+  {
+    Future<std::tuple<Nothing, Nothing>> pulls = process::collect(
+        pullDockerImage(DOCKER_TEST_IMAGE),
+        pullDockerImage(DOCKER_INKY_IMAGE));
+
+    // The pull should only need to happen once since we don't delete the
+    // image. So, we only log the warning once.
+    LOG_FIRST_N(WARNING, 1) << "Pulling " << string(DOCKER_TEST_IMAGE)
+                            << " and " << string(DOCKER_INKY_IMAGE) << ". "
+                            << "This might take a while...";
+
+    // The Windows images are ~200 MB, while the Linux images are ~2MB, so
+    // hopefully this is enough time for the Windows images. There should
+    // be some parallelism too, since we're pulling them simultaneously and
+    // they share the same base Windows layer.
+    AWAIT_READY_FOR(pulls, Minutes(10));
+  }
+
   virtual void TearDown()
   {
     Try<Owned<Docker>> docker = Docker::create(
@@ -297,7 +336,7 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_Launch_Executor)
   EXPECT_SOME(termination.get());
 
   ASSERT_FALSE(
-    exists(docker, containerId.get(), ContainerState::RUNNING));
+    exists(docker, containerId.get(), ContainerState::RUNNING, false));
 }
 
 
@@ -414,7 +453,7 @@ TEST_F(DockerContainerizerTest, DISABLED_ROOT_DOCKER_Launch_Executor_Bridged)
   EXPECT_SOME(termination.get());
 
   ASSERT_FALSE(
-    exists(docker, containerId.get(), ContainerState::RUNNING));
+    exists(docker, containerId.get(), ContainerState::RUNNING, false));
 }
 #endif // __linux__
 
@@ -476,7 +515,7 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_Launch)
       SLEEP_COMMAND(1000));
 
   // TODO(tnachen): Use local image to test if possible.
-  task.mutable_container()->CopyFrom(createDockerInfo("alpine"));
+  task.mutable_container()->CopyFrom(createDockerInfo(DOCKER_TEST_IMAGE));
 
   Future<ContainerID> containerId;
   EXPECT_CALL(dockerContainerizer, launch(_, _, _, _))
@@ -555,7 +594,7 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_Launch)
   EXPECT_SOME(termination.get());
 
   ASSERT_FALSE(
-    exists(docker, containerId.get(), ContainerState::RUNNING));
+    exists(docker, containerId.get(), ContainerState::RUNNING, false));
 }
 
 
@@ -617,15 +656,20 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_MaxCompletionTime)
   TaskInfo task =
     createTask(offer.slave_id(), offer.resources(), SLEEP_COMMAND(1000));
 
-  // Set a `max_completion_time` for 2 seconds. Hopefully this should not
-  // block test too long and still keep it reliable.
+  // Set a `max_completion_time` for 10 seconds on Windows and 2 seconds on
+  // other platforms. Hopefully this should not block test too long and still
+  // keep it reliable.
+#ifdef __WINDOWS__
+  task.mutable_max_completion_time()->set_nanoseconds(Seconds(10).ns());
+#else
   task.mutable_max_completion_time()->set_nanoseconds(Seconds(2).ns());
+#endif // __WINDOWS__
 
   ContainerInfo containerInfo;
   containerInfo.set_type(ContainerInfo::DOCKER);
 
   // TODO(tnachen): Use local image to test if possible.
-  task.mutable_container()->CopyFrom(createDockerInfo("alpine"));
+  task.mutable_container()->CopyFrom(createDockerInfo(DOCKER_TEST_IMAGE));
 
   Future<ContainerID> containerId;
   EXPECT_CALL(dockerContainerizer, launch(_, _, _, _))
@@ -721,7 +765,7 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_Kill)
       SLEEP_COMMAND(1000));
 
   // TODO(tnachen): Use local image to test if possible.
-  task.mutable_container()->CopyFrom(createDockerInfo("alpine"));
+  task.mutable_container()->CopyFrom(createDockerInfo(DOCKER_TEST_IMAGE));
 
   Future<ContainerID> containerId;
   EXPECT_CALL(dockerContainerizer, launch(_, _, _, _))
@@ -766,7 +810,7 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_Kill)
   EXPECT_EQ(0, termination.get()->status());
 
   ASSERT_FALSE(
-    exists(docker, containerId.get(), ContainerState::RUNNING));
+    exists(docker, containerId.get(), ContainerState::RUNNING, false));
 
   driver.stop();
   driver.join();
@@ -839,7 +883,7 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_TaskKillingCapability)
       SLEEP_COMMAND(1000));
 
   // TODO(tnachen): Use local image to test if possible.
-  task.mutable_container()->CopyFrom(createDockerInfo("alpine"));
+  task.mutable_container()->CopyFrom(createDockerInfo(DOCKER_TEST_IMAGE));
 
   Future<ContainerID> containerId;
   EXPECT_CALL(dockerContainerizer, launch(_, _, _, _))
@@ -884,7 +928,7 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_TaskKillingCapability)
   EXPECT_SOME(termination.get());
 
   ASSERT_FALSE(
-    exists(docker, containerId.get(), ContainerState::RUNNING));
+    exists(docker, containerId.get(), ContainerState::RUNNING, false));
 
   driver.stop();
   driver.join();
@@ -946,7 +990,11 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_Usage)
 
   CommandInfo command;
   // Run a CPU intensive command, so we can measure utime and stime later.
+#ifdef __WINDOWS__
+  command.set_value("for /L %n in (1, 0, 2) do rem");
+#else
   command.set_value("dd if=/dev/zero of=/dev/null");
+#endif // __WINDOWS__
 
   TaskInfo task = createTask(
       offers->front().slave_id(),
@@ -954,7 +1002,7 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_Usage)
       command);
 
   // TODO(tnachen): Use local image to test if possible.
-  task.mutable_container()->CopyFrom(createDockerInfo("alpine"));
+  task.mutable_container()->CopyFrom(createDockerInfo(DOCKER_TEST_IMAGE));
 
   Future<ContainerID> containerId;
   EXPECT_CALL(dockerContainerizer, launch(_, _, _, _))
@@ -1008,9 +1056,12 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_Usage)
   EXPECT_EQ(2.0 + slave::DEFAULT_EXECUTOR_CPUS, statistics.cpus_limit());
   EXPECT_EQ((Gigabytes(1) + slave::DEFAULT_EXECUTOR_MEM).bytes(),
             statistics.mem_limit_bytes());
+#ifndef __WINDOWS__
+  // These aren't provided by the Windows Container APIs, so skip them.
   EXPECT_LT(0, statistics.cpus_user_time_secs());
   EXPECT_LT(0, statistics.cpus_system_time_secs());
   EXPECT_GT(statistics.mem_rss_bytes(), 0u);
+#endif // __WINDOWS__
 
   Future<Option<ContainerTermination>> termination =
     dockerContainerizer.wait(containerId.get());
@@ -1227,10 +1278,10 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_Recover)
   Resources resources = Resources::parse("cpus:1;mem:512").get();
 
   // TODO(tnachen): Use local image to test if possible.
-  ContainerInfo containerInfo = createDockerInfo("alpine");
+  ContainerInfo containerInfo = createDockerInfo(DOCKER_TEST_IMAGE);
 
   CommandInfo commandInfo;
-  commandInfo.set_value("sleep 1000");
+  commandInfo.set_value(SLEEP_COMMAND(1000));
 
   Try<Docker::RunOptions> runOptions = Docker::RunOptions::create(
       containerInfo,
@@ -1309,7 +1360,7 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_Recover)
   EXPECT_NONE(termination2.get());
 
   // Expect the orphan to be stopped!
-  AWAIT_EXPECT_WEXITSTATUS_EQ(128 + SIGKILL, orphanRun);
+  assertDockerKillStatus(orphanRun);
 }
 
 
@@ -1358,10 +1409,10 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_KillOrphanContainers)
   Resources resources = Resources::parse("cpus:1;mem:512").get();
 
   // TODO(tnachen): Use local image to test if possible.
-  ContainerInfo containerInfo = createDockerInfo("alpine");
+  ContainerInfo containerInfo = createDockerInfo(DOCKER_TEST_IMAGE);
 
   CommandInfo commandInfo;
-  commandInfo.set_value("sleep 1000");
+  commandInfo.set_value(SLEEP_COMMAND(1000));
 
   Try<Docker::RunOptions> runOptions = Docker::RunOptions::create(
       containerInfo,
@@ -1438,9 +1489,10 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_KillOrphanContainers)
 
   AWAIT_READY(termination2);
   EXPECT_NONE(termination2.get());
-  ASSERT_FALSE(exists(docker, orphanContainerId));
+  ASSERT_FALSE(
+      exists(docker, orphanContainerId, ContainerState::EXISTS, false));
 
-  AWAIT_EXPECT_WEXITSTATUS_EQ(128 + SIGKILL, orphanRun);
+  assertDockerKillStatus(orphanRun);
 }
 
 
@@ -1545,10 +1597,10 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_SkipRecoverMalformedUUID)
   // TODO(tnachen): Use local image to test if possible.
 
   CommandInfo commandInfo;
-  commandInfo.set_value("sleep 1000");
+  commandInfo.set_value(SLEEP_COMMAND(1000));
 
   Try<Docker::RunOptions> runOptions = Docker::RunOptions::create(
-      createDockerInfo("alpine"),
+      createDockerInfo(DOCKER_TEST_IMAGE),
       commandInfo,
       container,
       flags.work_dir,
@@ -1584,6 +1636,8 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_SkipRecoverMalformedUUID)
 }
 
 
+// TOOD(akagup): Persistent volumes aren't implemented on Windows, but these
+// tests should be enabled once we implement them. See MESOS-5461.
 #ifdef __linux__
 // This test verifies that we can launch a docker container with
 // persistent volume.
@@ -1712,7 +1766,7 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_LaunchWithPersistentVolumes)
   EXPECT_SOME(termination.get());
 
   ASSERT_FALSE(
-    exists(docker, containerId.get(), ContainerState::RUNNING));
+    exists(docker, containerId.get(), ContainerState::RUNNING, false));
 
   const string& volumePath = getPersistentVolumePath(
       flags.work_dir,
@@ -2038,7 +2092,8 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_RecoverOrphanedPersistentVolumes)
 
   slave->reset();
 
-  EXPECT_FALSE(exists(docker, containerId.get()));
+  EXPECT_FALSE(
+      exists(docker, containerId.get(), ContainerState::EXISTS, false));
 }
 #endif // __linux__
 
@@ -2102,14 +2157,24 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_Logs)
 
   string uuid = id::UUID::random().toString();
 
+  CommandInfo command;
+#ifdef __WINDOWS__
+  // We avoid spaces in `echo` since `echo` in `cmd.exe` treats spaces
+  // in the argument as literal spaces so `echo X<SPACE>` outputs X<SPACE>.
+  // We don't use powershell here since `Write-Error` is verbose and causes
+  // the script to return a failure.
+  command.set_value(
+      "echo out" + uuid + "&"
+      "(echo err" + uuid + ")1>&2");
+#else
   // NOTE: We prefix `echo` with `unbuffer` so that we can immediately
   // flush the output of `echo`.  This mitigates a race in Docker where
   // it mangles reads from stdout/stderr and commits suicide.
   // See MESOS-4676 for more information.
-  CommandInfo command;
   command.set_value(
       "unbuffer echo out" + uuid + " ; "
       "unbuffer echo err" + uuid + " 1>&2");
+#endif // __WINDOWS__
 
   TaskInfo task = createTask(
       offers->front().slave_id(),
@@ -2117,10 +2182,17 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_Logs)
       command);
 
   // TODO(tnachen): Use local image to test if possible.
+#ifdef __WINDOWS__
+  const ContainerInfo containerInfo =
+    createDockerInfo(DOCKER_TEST_IMAGE);
+#else
   // NOTE: This is an image that is exactly
   // `docker run -t -i alpine /bin/sh -c "apk add --update expect"`.
-  task.mutable_container()->CopyFrom(
-      createDockerInfo("mesosphere/alpine-expect"));
+  const ContainerInfo containerInfo =
+    createDockerInfo("mesosphere/alpine-expect");
+#endif // __WINDOWS__
+
+  task.mutable_container()->CopyFrom(containerInfo);
 
   Future<ContainerID> containerId;
   Future<ContainerConfig> containerConfig;
@@ -2247,7 +2319,7 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_Default_CMD)
       command);
 
   // TODO(tnachen): Use local image to test if possible.
-  task.mutable_container()->CopyFrom(createDockerInfo("mesosphere/inky"));
+  task.mutable_container()->CopyFrom(createDockerInfo(DOCKER_INKY_IMAGE));
 
   Future<ContainerID> containerId;
   Future<ContainerConfig> containerConfig;
@@ -2375,7 +2447,7 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_Default_CMD_Override)
       command);
 
   // TODO(tnachen): Use local image to test if possible.
-  task.mutable_container()->CopyFrom(createDockerInfo("mesosphere/inky"));
+  task.mutable_container()->CopyFrom(createDockerInfo(DOCKER_INKY_IMAGE));
 
   Future<ContainerID> containerId;
   Future<ContainerConfig> containerConfig;
@@ -2507,7 +2579,7 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_Default_CMD_Args)
       command);
 
   // TODO(tnachen): Use local image to test if possible.
-  task.mutable_container()->CopyFrom(createDockerInfo("mesosphere/inky"));
+  task.mutable_container()->CopyFrom(createDockerInfo(DOCKER_INKY_IMAGE));
 
   Future<ContainerID> containerId;
   Future<ContainerConfig> containerConfig;
@@ -2630,7 +2702,7 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_SlaveRecoveryTaskContainer)
       SLEEP_COMMAND(1000));
 
   // TODO(tnachen): Use local image to test if possible.
-  task.mutable_container()->CopyFrom(createDockerInfo("alpine"));
+  task.mutable_container()->CopyFrom(createDockerInfo(DOCKER_TEST_IMAGE));
 
   Future<ContainerID> containerId;
   EXPECT_CALL(*dockerContainerizer, launch(_, _, _, _))
@@ -2712,11 +2784,14 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_SlaveRecoveryTaskContainer)
   EXPECT_SOME(termination.get());
 }
 
-
+#ifndef __WINDOWS__
 // The slave is stopped before the first update for a task is received
 // from the executor. When it comes back up we make sure the executor
 // reregisters and the slave properly sends the update.
 //
+// The test is removed on Windows, because the `mesosphere/test-executor`
+// image doesn't work on Windows and probably won't ever be ported.
+//
 // TODO(benh): This test is currently disabled because the executor
 // inside the image mesosphere/test-executor does not properly set the
 // executor PID that is uses during registration, so when the new
@@ -2879,13 +2954,19 @@ TEST_F(DockerContainerizerTest,
   driver.stop();
   driver.join();
 }
+#endif // __WINDOWS__
 
 
 // This test verifies that port mapping with bridge network is
 // exposing the host port to the container port, by sending data
 // to the host port and receiving it in the container by listening
 // to the mapped container port.
-TEST_F(DockerContainerizerTest, ROOT_DOCKER_NC_PortMapping)
+//
+// TODO(akagup): This test requres netcat on the Windows host before
+// it can be ported. We could provide a build of netcat or just replace
+// it with powershell for this test.
+TEST_F_TEMP_DISABLED_ON_WINDOWS(
+    DockerContainerizerTest, ROOT_DOCKER_NC_PortMapping)
 {
   Try<Owned<cluster::Master>> master = StartMaster();
   ASSERT_SOME(master);
@@ -3034,11 +3115,13 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_NC_PortMapping)
   EXPECT_SOME(termination.get());
 }
 
-
+#ifndef __WINDOWS__
 // This test verifies that sandbox with ':' in the path can still
 // run successfully. This a limitation of the Docker CLI where
 // the volume map parameter treats colons (:) as separators,
 // and incorrectly separates the sandbox directory.
+//
+// On Windows, colons aren't a legal path character, so this test is skipped.
 TEST_F(DockerContainerizerTest, ROOT_DOCKER_LaunchSandboxWithColon)
 {
   Try<Owned<cluster::Master>> master = StartMaster();
@@ -3133,6 +3216,7 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_LaunchSandboxWithColon)
   AWAIT_READY(termination);
   EXPECT_SOME(termination.get());
 }
+#endif // __WINDOWS__
 
 
 TEST_F(DockerContainerizerTest, ROOT_DOCKER_DestroyWhileFetching)
@@ -3206,7 +3290,7 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_DestroyWhileFetching)
       SLEEP_COMMAND(1000));
 
   // TODO(tnachen): Use local image to test if possible.
-  task.mutable_container()->CopyFrom(createDockerInfo("alpine"));
+  task.mutable_container()->CopyFrom(createDockerInfo(DOCKER_TEST_IMAGE));
 
   Future<TaskStatus> statusFailed;
   EXPECT_CALL(sched, statusUpdate(&driver, _))
@@ -3312,7 +3396,7 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_DestroyWhilePulling)
       SLEEP_COMMAND(1000));
 
   // TODO(tnachen): Use local image to test if possible.
-  task.mutable_container()->CopyFrom(createDockerInfo("alpine"));
+  task.mutable_container()->CopyFrom(createDockerInfo(DOCKER_TEST_IMAGE));
 
   Future<TaskStatus> statusFailed;
   EXPECT_CALL(sched, statusUpdate(&driver, _))
@@ -3438,9 +3522,9 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_ExecutorCleanupWhenLaunchFailed)
   TaskInfo task = createTask(
       offers->front().slave_id(),
       offers->front().resources(),
-      "ls");
+      "exit 0");
 
-  task.mutable_container()->CopyFrom(createDockerInfo("alpine"));
+  task.mutable_container()->CopyFrom(createDockerInfo(DOCKER_TEST_IMAGE));
 
   Future<TaskStatus> statusGone;
   EXPECT_CALL(sched, statusUpdate(&driver, _))
@@ -3532,9 +3616,9 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_FetchFailure)
   TaskInfo task = createTask(
       offers->front().slave_id(),
       offers->front().resources(),
-      "ls");
+      "exit 0");
 
-  task.mutable_container()->CopyFrom(createDockerInfo("alpine"));
+  task.mutable_container()->CopyFrom(createDockerInfo(DOCKER_TEST_IMAGE));
 
   Future<TaskStatus> statusFailed;
   EXPECT_CALL(sched, statusUpdate(&driver, _))
@@ -3629,9 +3713,9 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_DockerPullFailure)
   TaskInfo task = createTask(
       offers->front().slave_id(),
       offers->front().resources(),
-      "ls");
+      "exit 0");
 
-  task.mutable_container()->CopyFrom(createDockerInfo("alpine"));
+  task.mutable_container()->CopyFrom(createDockerInfo(DOCKER_TEST_IMAGE));
 
   Future<TaskStatus> statusFailed;
   EXPECT_CALL(sched, statusUpdate(&driver, _))
@@ -3736,7 +3820,11 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_DockerInspectDiscard)
   executorId.set_value("e1");
 
   CommandInfo command;
+#ifdef __WINDOWS__
+  command.set_value(SLEEP_COMMAND(1000));
+#else
   command.set_value("/bin/test-executor");
+#endif // __WINDOWS__
 
   TaskInfo task = createTask(
       offers->front().slave_id(),
@@ -3745,8 +3833,13 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_DockerInspectDiscard)
       executorId);
 
   // TODO(tnachen): Use local image to test if possible.
-  task.mutable_executor()->mutable_container()->CopyFrom(
-      createDockerInfo("tnachen/test-executor"));
+#ifdef __WINDOWS__
+  const ContainerInfo containerInfo = createDockerInfo(DOCKER_TEST_IMAGE);
+#else
+  const ContainerInfo containerInfo = createDockerInfo("tnachen/test-executor");
+#endif // __WINDOWS__
+
+  task.mutable_executor()->mutable_container()->CopyFrom(containerInfo);
 
   Future<TaskStatus> statusFailed;
   EXPECT_CALL(sched, statusUpdate(&driver, _))
@@ -3862,29 +3955,45 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_NoTransitionFromKillingToRunning)
 
   const uint16_t testPort = getFreePort().get();
 
+#ifdef __WINDOWS__
+  // On Windows, we will do a command health check instead of a TCP one,
+  // so that this test will work on Windows 10 (Hyper-V isolation) containers.
+  const string command = SLEEP_COMMAND(1000);
+#else
   // Launch a HTTP server until SIGTERM is received, then sleep for
   // 15 seconds to let the health check fail.
   const string command = strings::format(
       "trap \"sleep 15\" SIGTERM && nc -lk -p %u -e echo",
       testPort).get();
+#endif // __WINDOWS__
 
   TaskInfo task = createTask(offers->front(), command);
 
-  // The docker container runs in host network mode.
-  //
   // TODO(tnachen): Use local image to test if possible.
-  ContainerInfo containerInfo = createDockerInfo("alpine");
+  ContainerInfo containerInfo = createDockerInfo(DOCKER_TEST_IMAGE);
 
+  // On Linux, the docker container runs in host network mode.
+#ifndef __WINDOWS__
   containerInfo.mutable_docker()->set_network(
       ContainerInfo::DockerInfo::HOST);
+#endif // __WINDOWS__
 
   task.mutable_container()->CopyFrom(containerInfo);
 
-  // Set `grace_period_seconds` here because it takes some time to launch
-  // Netcat to serve requests.
   HealthCheck healthCheck;
+#ifdef __WINDOWS__
+  healthCheck.set_type(HealthCheck::COMMAND);
+
+  // The first `mkdir` will succeed, but the later ones will fail, so we get
+  // the same behavior as the Linux test.
+  healthCheck.mutable_command()->set_value("mkdir C:\\healthcheck-test");
+#else
   healthCheck.set_type(HealthCheck::TCP);
   healthCheck.mutable_tcp()->set_port(testPort);
+#endif // __WINDOWS__
+
+  // Set `grace_period_seconds` here because it takes some time to launch
+  // Netcat to serve requests.
   healthCheck.set_delay_seconds(0);
   healthCheck.set_grace_period_seconds(15);
   healthCheck.set_interval_seconds(0);
@@ -3950,9 +4059,14 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_NoTransitionFromKillingToRunning)
 }
 
 
+#ifndef __WINDOWS__
 // This test ensures that a task will transition from `TASK_KILLING`
 // to `TASK_KILLED` rather than `TASK_FINISHED` when it is killed,
 // even if it returns an "EXIT_STATUS" of 0 on receiving a SIGTERM.
+//
+// This test is ignored on Windows, since Windows containers seem to
+// always return `STATUS_CONTROL_C_EXIT` and `STATUS_UNSUCCESSFUL` for
+// graceful and forceful shutdown.
 TEST_F(DockerContainerizerTest, ROOT_DOCKER_NoTransitionFromKillingToFinished)
 {
   Shared<Docker> docker(new MockDocker(
@@ -4066,6 +4180,7 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_NoTransitionFromKillingToFinished)
   AWAIT_READY(termination);
   EXPECT_SOME(termination.get());
 }
+#endif // __WINDOWS__
 
 
 // This test ensures that when `cgroups_enable_cfs` is set on agent,
@@ -4191,9 +4306,12 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_CGROUPS_CFS_CgroupsEnableCFS)
 #endif // __linux__
 
 
+#ifndef __WINDOWS__
 // Run a task as non root while inheriting this ownership from the
 // framework supplied default user. Tests if the sandbox "stdout"
 // is correctly owned and writeable by the tasks user.
+// This test isn't run on Windows, because the `switch_user` flag
+// isn't supported.
 TEST_F(DockerContainerizerTest,
        ROOT_DOCKER_UNPRIVILEGED_USER_NonRootSandbox)
 {
@@ -4307,7 +4425,7 @@ TEST_F(DockerContainerizerTest,
   EXPECT_SOME(termination.get());
 
   ASSERT_FALSE(
-    exists(docker, containerId.get(), ContainerState::RUNNING));
+    exists(docker, containerId.get(), ContainerState::RUNNING, false));
 
   // Check that the sandbox was written to.
   const string sandboxDirectory = slave::paths::getExecutorRunPath(
@@ -4338,6 +4456,7 @@ TEST_F(DockerContainerizerTest,
 
   EXPECT_TRUE(strings::contains(stdout.get(), "foo"));
 }
+#endif // __WINDOWS__
 
 
 // This test verifies the DNS configuration of the Docker container
@@ -4353,6 +4472,23 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_DefaultDNS)
   Shared<Docker> docker(mockDocker);
 
   slave::Flags flags = CreateSlaveFlags();
+
+#ifdef __WINDOWS__
+  // --dns-option and --dns-search are not supported on Windows.
+  // See https://docs.microsoft.com/en-us/virtualization/windowscontainers/manage-containers/container-networking // NOLINT(whitespace/line_length)
+  Try<ContainerDNSInfo> parse = flags::parse<ContainerDNSInfo>(
+      R"~(
+      {
+        "docker": [
+          {
+            "network_mode": "BRIDGE",
+            "dns": {
+              "nameservers": [ "8.8.8.8", "8.8.4.4" ]
+            }
+          }
+        ]
+      })~");
+#else
   Try<ContainerDNSInfo> parse = flags::parse<ContainerDNSInfo>(
       R"~(
       {
@@ -4367,6 +4503,7 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_DefaultDNS)
           }
         ]
       })~");
+#endif // __WINDOWS__
 
   ASSERT_SOME(parse);
 
@@ -4417,7 +4554,7 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_DefaultDNS)
       SLEEP_COMMAND(1000));
 
   // TODO(tnachen): Use local image to test if possible.
-  ContainerInfo containerInfo = createDockerInfo("alpine");
+  ContainerInfo containerInfo = createDockerInfo(DOCKER_TEST_IMAGE);
 
   containerInfo.mutable_docker()->set_network(
       ContainerInfo::DockerInfo::BRIDGE);
@@ -4460,6 +4597,7 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_DefaultDNS)
 
   EXPECT_EQ(inspect->dns, defaultDNS);
 
+#ifndef __WINDOWS__
   vector<string> defaultDNSSearch;
   std::copy(
       flags.default_container_dns->docker(0).dns().search().begin(),
@@ -4475,6 +4613,7 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_DefaultDNS)
       std::back_inserter(defaultDNSOption));
 
   EXPECT_EQ(inspect->dnsOptions, defaultDNSOption);
+#endif // __WINDOWS__
 
   Future<Option<ContainerTermination>> termination =
     dockerContainerizer.wait(containerId.get());
@@ -4489,6 +4628,9 @@ TEST_F(DockerContainerizerTest, ROOT_DOCKER_DefaultDNS)
 
 // Fixture for testing IPv6 support for docker containers on host network.
 //
+// TODO(akagup): Windows containers do not support IPv6, but they should
+// in the future, so enable these when IPv6 is supported. See MESOS-8566.
+//
 // TODO(asridharan): Currently in the `Setup` and `TearDown` methods
 // of this class we re-initialize libprocess to take an IPv6 address.
 // Ideally, we should be moving this into a more general test fixture
@@ -4526,7 +4668,9 @@ protected:
 // is assumed to have an IPv4 address and an IPv6 address. The test
 // passes if the Mesos state correctly exposes both the IPv4 and IPv6
 // address.
-TEST_F(DockerContainerizerIPv6Test, ROOT_DOCKER_LaunchIPv6HostNetwork)
+TEST_F_TEMP_DISABLED_ON_WINDOWS(
+    DockerContainerizerIPv6Test,
+    ROOT_DOCKER_LaunchIPv6HostNetwork)
 {
   Try<Owned<cluster::Master>> master = StartMaster();
   ASSERT_SOME(master);
@@ -4686,7 +4830,7 @@ TEST_F(DockerContainerizerIPv6Test, ROOT_DOCKER_LaunchIPv6HostNetwork)
   EXPECT_SOME(termination.get());
 
   ASSERT_FALSE(
-    exists(docker, containerId.get(), ContainerState::RUNNING));
+    exists(docker, containerId.get(), ContainerState::RUNNING, false));
 }
 
 
@@ -4712,7 +4856,7 @@ protected:
 // Launches a docker container on the docker user network. The docker network
 // is assumed to have an IPv4 address and an IPv6 address. The test passes if
 // the Mesos state correctly exposes both the IPv4 and IPv6 address.
-TEST_F(
+TEST_F_TEMP_DISABLED_ON_WINDOWS(
     DockerContainerizerIPv6UserNetworkTest,
     ROOT_DOCKER_USERNETWORK_LaunchIPv6Container)
 {
@@ -4881,16 +5025,22 @@ TEST_F(
   EXPECT_SOME(termination.get());
 
   ASSERT_FALSE(
-    exists(docker, containerId.get(), ContainerState::RUNNING));
+    exists(docker, containerId.get(), ContainerState::RUNNING, false));
 }
 
 
 class HungDockerTest : public DockerContainerizerTest
 {
 public:
-  const string testDockerEnvFile = "test-docker.env";
   const string testDockerBinary = "docker";
+#ifdef __WINDOWS__
+  const string testDockerScript = "test-docker.bat";
+  const string testDockerEnvFile = "test-docker-env.bat";
+#else
   const string testDockerScript = "test-docker.sh";
+  const string testDockerEnvFile = "test-docker.env";
+#endif // __WINDOWS__
+
   string commandsEnv;
   string delayEnv;
 
@@ -4908,14 +5058,26 @@ public:
     // TODO(greggomann): This write operation is not atomic, which means an
     // ill-timed write may cause the shell script to be invoked when this
     // file is in an unintended state. We should make this atomic.
+
     Try<Nothing> write =
+#ifdef __WINDOWS__
+      os::write(testDockerEnvFile, commandsEnv + "\r\n" + delayEnv);
+#else
       os::write(testDockerEnvFile, commandsEnv + "\n" + delayEnv);
+#endif // __WINDOWS__
+
     ASSERT_SOME(write);
   }
 
   void setDelayedCommands(const std::vector<string>& commands)
   {
-    commandsEnv = "DELAYED_COMMANDS=( ";
+#ifdef __WINDOWS__
+    commandsEnv = "set ";
+#else
+    commandsEnv = "";
+#endif // __WINDOWS__
+
+    commandsEnv += "DELAYED_COMMANDS=( ";
     foreach (const string& command, commands) {
       commandsEnv += (command + " ");
     }
@@ -4926,7 +5088,13 @@ public:
 
   void setDelay(const int seconds)
   {
-    delayEnv = "DELAY_SECONDS=" + stringify(seconds);
+#ifdef __WINDOWS__
+    delayEnv = "set ";
+#else
+    delayEnv = "";
+#endif // __WINDOWS__
+
+    delayEnv += "DELAY_SECONDS=" + stringify(seconds);
 
     writeEnv();
   }
@@ -4936,6 +5104,20 @@ public:
     DockerContainerizerTest::SetUp();
 
     // Write a wrapper script which allows us to delay Docker commands.
+#ifdef __WINDOWS__
+    const string dockerScriptText =
+      "@echo off\r\n"
+      "setlocal enabledelayedexpansion\r\n"
+      "call \"" + path::join(os::getcwd(), testDockerEnvFile) + "\"\r\n"
+      "set ACTIVE_COMMAND=%3\r\n"
+      "if not defined DELAYED_COMMANDS set DELAYED_COMMANDS=()\r\n"
+      "for %%G in %DELAYED_COMMANDS% do (\r\n"
+      "  if %ACTIVE_COMMAND% == %%G (\r\n"
+      "    ping -n %DELAY_SECONDS% 127.0.0.1 > NUL\r\n"
+      "  )\r\n"
+      ")\r\n" +
+      testDockerBinary + " %*\r\n";
+#else
     const string dockerScriptText =
       "#!/usr/bin/env bash\n"
       "source " + stringify(path::join(os::getcwd(), testDockerEnvFile)) + "\n"
@@ -4946,13 +5128,16 @@ public:
       "  fi\n"
       "done\n" +
       testDockerBinary + " \"$@\"\n";
+#endif // __WINDOWS__
 
     Try<Nothing> write = os::write(testDockerScript, dockerScriptText);
     ASSERT_SOME(write);
 
+#ifndef __WINDOWS__
     Try<Nothing> chmod = os::chmod(
         testDockerScript, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
     ASSERT_SOME(chmod);
+#endif // __WINDOWS__
 
     // Set a very long delay by default to simulate an indefinitely
     // hung Docker daemon.
@@ -4961,8 +5146,7 @@ public:
 };
 
 
-TEST_F_TEMP_DISABLED_ON_WINDOWS(
-    HungDockerTest, ROOT_DOCKER_InspectHungDuringPull)
+TEST_F(HungDockerTest, ROOT_DOCKER_InspectHungDuringPull)
 {
   Try<Owned<cluster::Master>> master = StartMaster();
   ASSERT_SOME(master);
@@ -5029,7 +5213,7 @@ TEST_F_TEMP_DISABLED_ON_WINDOWS(
       SLEEP_COMMAND(1000));
 
   // TODO(tnachen): Use local image to test if possible.
-  task.mutable_container()->CopyFrom(createDockerInfo("alpine"));
+  task.mutable_container()->CopyFrom(createDockerInfo(DOCKER_TEST_IMAGE));
 
   Future<ContainerID> containerId;
   EXPECT_CALL(dockerContainerizer, launch(_, _, _, _))


[16/16] mesos git commit: Windows: Made `PipeLargeOutput` test work with IOCP backend.

Posted by an...@apache.org.
Windows: Made `PipeLargeOutput` test work with IOCP backend.

The `PipeLargeOutput` test was calling `io::read`, which was returning
the wrong error since it operates at a higher level abstraction. The
test really just checks if a pipe EOF returns `ERROR_BROKEN_PIPE`, so
the test was changed to use the lower level `os::read`.

Review: https://reviews.apache.org/r/67392/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/a9066455
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/a9066455
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/a9066455

Branch: refs/heads/master
Commit: a906645522807ecc0322707026e22d78bb82e7ac
Parents: e2cba0e
Author: Akash Gupta <ak...@hotmail.com>
Authored: Wed Jun 27 14:30:17 2018 -0700
Committer: Andrew Schwartzmeyer <an...@schwartzmeyer.com>
Committed: Wed Jun 27 15:06:10 2018 -0700

----------------------------------------------------------------------
 3rdparty/libprocess/src/tests/subprocess_tests.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/a9066455/3rdparty/libprocess/src/tests/subprocess_tests.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/tests/subprocess_tests.cpp b/3rdparty/libprocess/src/tests/subprocess_tests.cpp
index b12490e..aa939a6 100644
--- a/3rdparty/libprocess/src/tests/subprocess_tests.cpp
+++ b/3rdparty/libprocess/src/tests/subprocess_tests.cpp
@@ -380,7 +380,9 @@ TEST_F(SubprocessTest, PipeLargeOutput)
   ::SetLastError(0);
 #endif // __WINDOWS__
 
-  AWAIT_EXPECT_EQ(output, io::read(s->out().get()));
+  // Read 1 more than the input size, so we can trigger the EOF error
+  // on Windows.
+  EXPECT_SOME_EQ(output, os::read(s->out().get(), 1 + output.size()));
 
 #ifdef __WINDOWS__
   // NOTE: On Windows, this is the end-of-file condition when reading


[12/16] mesos git commit: Added CMake `ENABLE_LIBWINIO` option for Windows IOCP backend.

Posted by an...@apache.org.
Added CMake `ENABLE_LIBWINIO` option for Windows IOCP backend.

This option will let us enable the new Windows IOCP backend for
libprocess. It can only be used on Windows, and it is mutually
exclusive with `ENABLE_LIBEVENT`. For now, it is disabled by default,
but this is expected to change, followed be a removal of libevent
support for Windows.

Review: https://reviews.apache.org/r/67391/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/6bf70a19
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/6bf70a19
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/6bf70a19

Branch: refs/heads/master
Commit: 6bf70a1956325c509d65e6bcdba094b1c6bdf795
Parents: 5fd78df
Author: Akash Gupta <ak...@hotmail.com>
Authored: Wed Jun 27 14:30:17 2018 -0700
Committer: Andrew Schwartzmeyer <an...@schwartzmeyer.com>
Committed: Wed Jun 27 15:06:10 2018 -0700

----------------------------------------------------------------------
 3rdparty/CMakeLists.txt          |  2 +-
 cmake/CompilationConfigure.cmake | 18 ++++++++++++++++--
 2 files changed, 17 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/6bf70a19/3rdparty/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt
index 5e46b41..b58996d 100644
--- a/3rdparty/CMakeLists.txt
+++ b/3rdparty/CMakeLists.txt
@@ -571,7 +571,7 @@ if (ENABLE_LIBEVENT)
       target_link_libraries(libevent INTERFACE ${LIBEVENT_LIB})
     endforeach ()
   endif ()
-else ()
+elseif (NOT ENABLE_LIBWINIO)
   # libev: Full-featured high-performance event loop.
   # https://github.com/enki/libev
   ###################################################

http://git-wip-us.apache.org/repos/asf/mesos/blob/6bf70a19/cmake/CompilationConfigure.cmake
----------------------------------------------------------------------
diff --git a/cmake/CompilationConfigure.cmake b/cmake/CompilationConfigure.cmake
index e2ee89a..61387d7 100644
--- a/cmake/CompilationConfigure.cmake
+++ b/cmake/CompilationConfigure.cmake
@@ -107,6 +107,11 @@ if (ENABLE_LIBEVENT)
 endif()
 
 option(
+  ENABLE_LIBWINIO
+  "Use Windows IOCP instead of libev as the core event loop implementation."
+  FALSE)
+
+option(
   ENABLE_SSL
   "Build libprocess with SSL support."
   FALSE)
@@ -226,12 +231,21 @@ if (WIN32 AND REBUNDLED)
     "the Internet, even though the `REBUNDLED` flag was set.")
 endif ()
 
-if (WIN32 AND (NOT ENABLE_LIBEVENT))
+if (WIN32 AND (NOT ENABLE_LIBEVENT AND NOT ENABLE_LIBWINIO))
   message(
     FATAL_ERROR
     "Windows builds of Mesos currently do not support libev, the default event "
     "loop used by Mesos.  To opt into using libevent, pass "
-    "`-DENABLE_LIBEVENT=1` as an argument when you run CMake.")
+    "`-DENABLE_LIBEVENT=1` as an argument when you run CMake."
+    "To opt into using the native Windows IOCP implementation instead, "
+    "pass `-DENABLE_LIBWINIO=1` as an argument.")
+endif ()
+
+if (ENABLE_LIBWINIO AND (NOT WIN32))
+  message(
+    FATAL_ERROR
+    "Libwinio, which is the Windows IOCP event loop, only works on Windows. "
+    "Please use libev or libevent instead on non-Windows platforms.")
 endif ()
 
 if (ENABLE_SSL AND (NOT ENABLE_LIBEVENT))


[02/16] mesos git commit: Windows: Ported `io_tests.cpp`.

Posted by an...@apache.org.
Windows: Ported `io_tests.cpp`.

With the IOCP backened implemented, `io_tests.cpp` is now ported to
Windows. Since the tests require async pipe IO, the tests are only run
if libprocess is compiled with the IOCP backened.

Review: https://reviews.apache.org/r/67393/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/11f8b6db
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/11f8b6db
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/11f8b6db

Branch: refs/heads/master
Commit: 11f8b6db9575d22c0ce221dcf19b7181dfc05b0d
Parents: a906645
Author: Akash Gupta <ak...@hotmail.com>
Authored: Wed Jun 27 14:30:18 2018 -0700
Committer: Andrew Schwartzmeyer <an...@schwartzmeyer.com>
Committed: Wed Jun 27 15:06:10 2018 -0700

----------------------------------------------------------------------
 3rdparty/libprocess/src/tests/CMakeLists.txt |   3 +
 3rdparty/libprocess/src/tests/io_tests.cpp   | 128 ++++++++++++++++------
 2 files changed, 95 insertions(+), 36 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/11f8b6db/3rdparty/libprocess/src/tests/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/tests/CMakeLists.txt b/3rdparty/libprocess/src/tests/CMakeLists.txt
index 400b773..25a34f9 100644
--- a/3rdparty/libprocess/src/tests/CMakeLists.txt
+++ b/3rdparty/libprocess/src/tests/CMakeLists.txt
@@ -48,6 +48,9 @@ if (NOT WIN32)
   list(APPEND PROCESS_TESTS_SRC
     io_tests.cpp
     reap_tests.cpp)
+elseif (ENABLE_LIBWINIO)
+  list(APPEND PROCESS_TESTS_SRC
+    io_tests.cpp)
 endif ()
 
 if (ENABLE_GRPC)

http://git-wip-us.apache.org/repos/asf/mesos/blob/11f8b6db/3rdparty/libprocess/src/tests/io_tests.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/tests/io_tests.cpp b/3rdparty/libprocess/src/tests/io_tests.cpp
index de7272b..d1a7463 100644
--- a/3rdparty/libprocess/src/tests/io_tests.cpp
+++ b/3rdparty/libprocess/src/tests/io_tests.cpp
@@ -21,6 +21,10 @@
 #include <stout/gtest.hpp>
 #include <stout/os.hpp>
 
+#include <stout/os/pipe.hpp>
+#include <stout/os/read.hpp>
+#include <stout/os/write.hpp>
+
 #include <stout/tests/utils.hpp>
 
 #include "encoder.hpp"
@@ -29,10 +33,15 @@ namespace io = process::io;
 
 using process::Future;
 
+using std::array;
 using std::string;
 
 class IOTest: public TemporaryDirectoryTest {};
 
+// Polling is the POSIX way to do async IO and Windows does not provide a
+// generic and scalable way to poll on arbitary `HANDLEs`, so this test is
+// removed on Windows.
+#ifndef __WINDOWS__
 TEST_F(IOTest, Poll)
 {
   int pipes[2];
@@ -53,15 +62,18 @@ TEST_F(IOTest, Poll)
   ASSERT_SOME(os::close(pipes[0]));
   ASSERT_SOME(os::close(pipes[1]));
 }
+#endif // __WINDOWS__
 
 
 TEST_F(IOTest, Read)
 {
-  int pipes[2];
   char data[3];
 
   // Create a blocking pipe.
-  ASSERT_NE(-1, ::pipe(pipes));
+  Try<array<int_fd, 2>> pipes_ = os::pipe();
+  ASSERT_SOME(pipes_);
+
+  array<int_fd, 2> pipes = pipes_.get();
 
   // Test on a blocking file descriptor.
   AWAIT_EXPECT_FAILED(io::read(pipes[0], data, 3));
@@ -73,9 +85,12 @@ TEST_F(IOTest, Read)
   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]));
+  pipes_ = os::pipe();
+  ASSERT_SOME(pipes_);
+  pipes = pipes_.get();
+
+  ASSERT_SOME(io::prepare_async(pipes[0]));
+  ASSERT_SOME(io::prepare_async(pipes[1]));
 
   // Test reading nothing.
   AWAIT_EXPECT_EQ(0u, io::read(pipes[0], data, 0));
@@ -90,7 +105,7 @@ TEST_F(IOTest, Read)
   future = io::read(pipes[0], data, 3);
   ASSERT_FALSE(future.isReady());
 
-  ASSERT_EQ(2, write(pipes[1], "hi", 2));
+  ASSERT_EQ(2, os::write(pipes[1], "hi", 2));
 
   AWAIT_ASSERT_EQ(2u, future);
   EXPECT_EQ('h', data[0]);
@@ -102,9 +117,10 @@ TEST_F(IOTest, Read)
 
   future.discard();
 
-  ASSERT_EQ(3, write(pipes[1], "omg", 3));
+  future = io::read(pipes[0], data, 3);
+  ASSERT_EQ(3, os::write(pipes[1], "omg", 3));
 
-  AWAIT_ASSERT_EQ(3u, io::read(pipes[0], data, 3)) << string(data, 2);
+  AWAIT_ASSERT_EQ(3u, future) << string(data, 2);
   EXPECT_EQ('o', data[0]);
   EXPECT_EQ('m', data[1]);
   EXPECT_EQ('g', data[2]);
@@ -146,17 +162,20 @@ TEST_F(IOTest, BufferedRead)
   ASSERT_SOME(os::close(fd.get()));
 
   // Now read from pipes.
-  int pipes[2];
+  Try<array<int_fd, 2>> pipes_ = os::pipe();
+  ASSERT_SOME(pipes_);
 
   // Test on a closed pipe.
-  ASSERT_NE(-1, ::pipe(pipes));
+  array<int_fd, 2> pipes = pipes_.get();
   ASSERT_SOME(os::close(pipes[0]));
   ASSERT_SOME(os::close(pipes[1]));
 
   AWAIT_EXPECT_FAILED(io::read(pipes[0]));
 
   // Test a successful read from the pipe.
-  ASSERT_NE(-1, ::pipe(pipes));
+  pipes_ = os::pipe();
+  ASSERT_SOME(pipes_);
+  pipes = pipes_.get();
 
   // At first, the future will not be ready until we write to and
   // close the pipe.
@@ -176,10 +195,10 @@ TEST_F(IOTest, BufferedRead)
 
 TEST_F(IOTest, Write)
 {
-  int pipes[2];
-
   // Create a blocking pipe.
-  ASSERT_NE(-1, ::pipe(pipes));
+  Try<array<int_fd, 2>> pipes_ = os::pipe();
+  ASSERT_SOME(pipes_);
+  array<int_fd, 2> pipes = pipes_.get();
 
   // Test on a blocking file descriptor.
   AWAIT_EXPECT_FAILED(io::write(pipes[1], (void*) "hi", 2));
@@ -191,15 +210,18 @@ TEST_F(IOTest, Write)
   AWAIT_EXPECT_FAILED(io::write(pipes[1], (void*) "hi", 2));
 
   // Create a nonblocking pipe.
-  ASSERT_NE(-1, ::pipe(pipes));
-  ASSERT_SOME(os::nonblock(pipes[0]));
-  ASSERT_SOME(os::nonblock(pipes[1]));
+  pipes_ = os::pipe();
+  ASSERT_SOME(pipes_);
+  pipes = pipes_.get();
+
+  ASSERT_SOME(io::prepare_async(pipes[0]));
+  ASSERT_SOME(io::prepare_async(pipes[1]));
 
   // Test writing nothing.
   AWAIT_EXPECT_EQ(0u, io::write(pipes[1], (void*) "hi", 0));
 
   // Test successful write.
-  AWAIT_EXPECT_EQ(2u, io::write(pipes[1], (void*) "hi", 2));
+  Future<size_t> future = io::write(pipes[1], (void*) "hi", 2);
 
   char data[2];
   AWAIT_EXPECT_EQ(2u, io::read(pipes[0], data, 2));
@@ -213,21 +235,50 @@ TEST_F(IOTest, Write)
 }
 
 
+#ifdef __WINDOWS__
+TEST_F(IOTest, BlockingWrite)
+#else
 // TODO(alexr): Enable after MESOS-973 is resolved.
 TEST_F(IOTest, DISABLED_BlockingWrite)
+#endif // __WINDOWS__
 {
-  int pipes[2];
-
-  // Create a nonblocking pipe.
-  ASSERT_NE(-1, ::pipe(pipes));
-  ASSERT_SOME(os::nonblock(pipes[0]));
-  ASSERT_SOME(os::nonblock(pipes[1]));
+  Try<array<int_fd, 2>> pipes_ = os::pipe();
+  ASSERT_SOME(pipes_);
+
+  array<int_fd, 2> pipes = pipes_.get();
+
+  // Get the pipe buffer size. On Windows, we can query this directly. On
+  // other platforms, we do non-blocking writes until we get `EAGAIN` or
+  // `EWOULDBLOCK`.
+#ifdef __WINDOWS__
+  DWORD outBufferSize;
+  const BOOL success = ::GetNamedPipeInfo(
+      pipes[0],       // Pipe `HANDLE`.
+      nullptr,        // Flags.
+      &outBufferSize, // Outbound (write) buffer size.
+      nullptr,        // Inbound (read) buffer size.
+      nullptr);       // Max instances of the named pipe.
+
+  ASSERT_TRUE(success);
+
+  size_t size = static_cast<size_t>(outBufferSize);
+  if (size < 4096) {
+    // On Windows, the buffer size can be very small and even 0, which will
+    // break this test, so we report that the buffer size is bigger if it's
+    // too small. This doesn't change the test semantics; all it does is
+    // make the test write a longer string.
+    size = 4096;
+  }
+#else
+  // Make pipes non-blocking.
+  ASSERT_SOME(io::prepare_async(pipes[0]));
+  ASSERT_SOME(io::prepare_async(pipes[1]));
 
   // Determine the pipe buffer size by writing until we block.
   size_t size = 0;
-  ssize_t length = 0;
-  while ((length = ::write(pipes[1], "data", 4)) >= 0) {
-    size += length;
+  ssize_t written = 0;
+  while ((written = ::write(pipes[1], "data", 4)) >= 0) {
+    size += written;
   }
 
   ASSERT_TRUE(errno == EAGAIN || errno == EWOULDBLOCK);
@@ -236,9 +287,13 @@ TEST_F(IOTest, DISABLED_BlockingWrite)
   ASSERT_SOME(os::close(pipes[1]));
 
   // Recreate a nonblocking pipe.
-  ASSERT_NE(-1, ::pipe(pipes));
-  ASSERT_SOME(os::nonblock(pipes[0]));
-  ASSERT_SOME(os::nonblock(pipes[1]));
+  pipes_ = os::pipe();
+  ASSERT_SOME(pipes_);
+  pipes = pipes_.get();
+#endif // __WINDOWS__
+
+  ASSERT_SOME(io::prepare_async(pipes[0]));
+  ASSERT_SOME(io::prepare_async(pipes[1]));
 
   // Create 8 pipe buffers worth of data. Try and write all the data
   // at once. Check that the future is pending after doing the
@@ -273,7 +328,7 @@ TEST_F(IOTest, DISABLED_BlockingWrite)
   // Now read all the data we wrote the first time and expect the
   // first future to succeed since the second future should have been
   // completely discarded.
-  length = 128; // To account for io::read above.
+  ssize_t length = 128; // To account for io::read above.
   while (length < static_cast<ssize_t>(data.size())) {
     Future<size_t> read = io::read(pipes[0], temp, 128);
     AWAIT_READY(read);
@@ -304,14 +359,15 @@ TEST_F(IOTest, Redirect)
 
   ASSERT_SOME(fd);
 
-  ASSERT_SOME(os::nonblock(fd.get()));
+  ASSERT_SOME(io::prepare_async(fd.get()));
 
   // Use a nonblocking pipe for doing the redirection.
-  int pipes[2];
+  Try<array<int_fd, 2>> pipes_ = os::pipe();
+  ASSERT_SOME(pipes_);
 
-  ASSERT_NE(-1, ::pipe(pipes));
-  ASSERT_SOME(os::nonblock(pipes[0]));
-  ASSERT_SOME(os::nonblock(pipes[1]));
+  array<int_fd, 2> pipes = pipes_.get();
+  ASSERT_SOME(io::prepare_async(pipes[0]));
+  ASSERT_SOME(io::prepare_async(pipes[1]));
 
   // Set up a redirect hook to also accumlate the data that we splice.
   string accumulated;


[11/16] mesos git commit: Windows: Implemented Windows IOCP async backend.

Posted by an...@apache.org.
Windows: Implemented Windows IOCP async backend.

The Windows IOCP async backend, called libwinio, provides a single
threaded event loop implementation that allows for async IO and timer
events. This library wraps the native Win32 async functions using
libprocess's primitives, which makes it easier to use and more type
safe.

Review: https://reviews.apache.org/r/67389/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/bfeda6b5
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/bfeda6b5
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/bfeda6b5

Branch: refs/heads/master
Commit: bfeda6b5f8686db1eaba566510bba813081852f4
Parents: 78244b5
Author: Akash Gupta <ak...@hotmail.com>
Authored: Wed Jun 27 14:30:15 2018 -0700
Committer: Andrew Schwartzmeyer <an...@schwartzmeyer.com>
Committed: Wed Jun 27 15:06:10 2018 -0700

----------------------------------------------------------------------
 3rdparty/libprocess/src/CMakeLists.txt       |   3 +-
 3rdparty/libprocess/src/windows/libwinio.cpp | 744 ++++++++++++++++++++++
 3rdparty/libprocess/src/windows/libwinio.hpp |  85 +++
 3 files changed, 831 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/bfeda6b5/3rdparty/libprocess/src/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/CMakeLists.txt b/3rdparty/libprocess/src/CMakeLists.txt
index 3544e91..5f617eb 100644
--- a/3rdparty/libprocess/src/CMakeLists.txt
+++ b/3rdparty/libprocess/src/CMakeLists.txt
@@ -52,7 +52,8 @@ set(PROCESS_SRC
 
 if (WIN32)
   list(APPEND PROCESS_SRC
-    windows/subprocess.cpp)
+    windows/subprocess.cpp
+    windows/libwinio.cpp) # TEMPORARY
 else ()
   list(APPEND PROCESS_SRC
     posix/subprocess.cpp)

http://git-wip-us.apache.org/repos/asf/mesos/blob/bfeda6b5/3rdparty/libprocess/src/windows/libwinio.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/windows/libwinio.cpp b/3rdparty/libprocess/src/windows/libwinio.cpp
new file mode 100644
index 0000000..85ee7ea
--- /dev/null
+++ b/3rdparty/libprocess/src/windows/libwinio.cpp
@@ -0,0 +1,744 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#include <memory>
+
+#include <stout/lambda.hpp>
+#include <stout/try.hpp>
+#include <stout/windows.hpp>
+
+#include <stout/os/int_fd.hpp>
+#include <stout/os/read.hpp>
+#include <stout/os/sendfile.hpp>
+#include <stout/os/write.hpp>
+
+#include <stout/windows/error.hpp>
+
+#include <process/address.hpp>
+#include <process/network.hpp>
+#include <process/process.hpp> // For process::initialize.
+
+#include "windows/libwinio.hpp"
+
+namespace process {
+namespace windows {
+
+// Key constants for IOCP `GetQueuedCompletionStatusEx`.
+constexpr DWORD KEY_QUIT = 0;
+constexpr DWORD KEY_IO = 1;
+constexpr DWORD KEY_TIMER = 2;
+
+
+// Definitions for handling for event loop timers.
+struct TimerOverlapped
+{
+  HANDLE timer;
+  LARGE_INTEGER time;
+  lambda::function<void()> callback;
+};
+
+
+// This ia a `TimerAPCProc` callback function that is called when the timer
+// set by `SetWaitableTimer` fires [1].  Note that `CALLBACK` is defined to be
+// `__stdcall` in the Windows SDK, since the Win32 APIs use the stdcall
+// calling convention.
+//
+// [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms686786(v=vs.85).aspx // NOLINT(whitespace/line_length)
+static void CALLBACK timer_apc(void* arg, DWORD timer_low, DWORD timer_high)
+{
+  TimerOverlapped* timer = reinterpret_cast<TimerOverlapped*>(arg);
+  timer->callback();
+  ::CloseHandle(timer->timer);
+  delete timer;
+}
+
+
+// Definitions for handling event loop IO.
+enum class IOType
+{
+  READ,
+  WRITE,
+  RECV,
+  SEND,
+  ACCEPT,
+  CONNECT,
+  SENDFILE
+};
+
+// Base overlapped struct that contains the required Win32 overlapped object,
+// the `HANDLE` where the IO was performed and the type of IO performed.
+struct IOOverlappedBase
+{
+  OVERLAPPED overlapped;
+  HANDLE handle;
+  IOType type;
+};
+
+// We keep `Promise<T>*` instead of `Promises<T>` so that we decouple the
+// Promise from the overlapped object. This is because to support cancellation,
+// we keep `std::shared_ptr` and `std::weak_ptr` copies of the overlapped
+// object pointers in the `Promise` callbacks, so when the callbacks are
+// cleaned up, we would also end up cleaning up the Promise itself, which can
+// cause memory corruption.
+template <typename T>
+struct IOOverlapped
+{
+  IOOverlapped(const IOOverlappedBase& _base, Promise<T>* _promise)
+    : base(_base), promise(_promise)
+  {}
+
+  IOOverlappedBase base;
+  Promise<T>* promise;
+};
+
+using IOOverlappedReadWrite = IOOverlapped<size_t>;
+using IOOverlappedConnect = IOOverlapped<Nothing>;
+
+// The async `Accept` is like `Connect` but with some additional buffers.
+// We can't use inheritance here because `IOOverlappedAccept` will not
+// be a C++ standard layout type, meaning that treating it like a C
+// struct in the IOCP code can lead to undefined behavior.
+struct IOOverlappedAccept
+{
+  IOOverlappedAccept(const IOOverlappedBase& _base, Promise<Nothing>* _promise)
+    : base(_base), promise(_promise)
+  {}
+
+  IOOverlappedBase base;
+  Promise<Nothing>* promise;
+
+  // `AcceptEx` needs a buffer size of atleast "16 bytes more than the size of
+  // the sockaddr structure for the transport protocol" [1] for the remote and
+  // local addresses.
+  // [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms737524(v=vs.85).aspx // NOLINT(whitespace/line_length)
+  unsigned char buf[2 * sizeof(SOCKADDR_STORAGE) + 32];
+};
+
+// The IOCP Win32 APIs use C structs and involve a lot of type unsafe casting,
+// so we ensure that these C++ structs can be safely used as C structs.
+static_assert(
+    std::is_standard_layout<IOOverlappedReadWrite>::value,
+    "IOOverlappedReadWrite must be a standard layout type");
+
+static_assert(
+    std::is_standard_layout<IOOverlappedConnect>::value,
+    "IOOverlappedConnect must be a standard layout type");
+
+static_assert(
+    std::is_standard_layout<IOOverlappedAccept>::value,
+    "IOOverlappedAccept must be a standard layout type");
+
+
+template <typename T>
+static void set_io_promise(Promise<T>* promise, const T& data, DWORD error)
+{
+  if (promise->future().hasDiscard()) {
+    promise->discard();
+  } else if (error == ERROR_SUCCESS) {
+    promise->set(data);
+  } else {
+    promise->fail("IO failed with error code: " + WindowsError(error).message);
+  }
+}
+
+
+static void handle_io(OVERLAPPED* overlapped_, DWORD bytes_transferred)
+{
+  // Overlapped objects to passed to this function should have been contained
+  // inside a `IOOverlappedBase`. So, we can use the `CONTAINING_RECORD` macro
+  // to get the pointer to the `IOOverlappedBase` struct from the `OVERLAPPED`
+  // pointer.
+  IOOverlappedBase* overlapped_base =
+    CONTAINING_RECORD(overlapped_, IOOverlappedBase, overlapped);
+
+  // Get the Win32 error code of the overlapped operation. The status code
+  // is actually in overlapped->overlapped->Internal, but it's the NT
+  // status code instead of the Win32 error.
+  DWORD error = ERROR_SUCCESS;
+  DWORD bytes;
+  const BOOL success = ::GetOverlappedResult(
+      overlapped_base->handle, &overlapped_base->overlapped, &bytes, FALSE);
+
+  if (!success) {
+    error = ::GetLastError();
+  } else {
+    // If the IO succeeded, these should be the same for sure.
+    CHECK_EQ(bytes, bytes_transferred);
+  }
+
+  switch (overlapped_base->type) {
+    case IOType::READ:
+    case IOType::RECV: {
+      IOOverlappedReadWrite* io_read =
+        CONTAINING_RECORD(overlapped_base, IOOverlappedReadWrite, base);
+
+      std::unique_ptr<Promise<size_t>> promise(io_read->promise);
+
+      // For reads, we need to make sure we ignore the EOF errors.
+      if (error == ERROR_BROKEN_PIPE || error == ERROR_HANDLE_EOF) {
+        set_io_promise(promise.get(), static_cast<size_t>(0), ERROR_SUCCESS);
+      } else {
+        set_io_promise(
+            promise.get(), static_cast<size_t>(bytes_transferred), error);
+      }
+      return;
+    }
+    case IOType::WRITE:
+    case IOType::SEND:
+    case IOType::SENDFILE: {
+      IOOverlappedReadWrite* io_write =
+        CONTAINING_RECORD(overlapped_base, IOOverlappedReadWrite, base);
+
+      std::unique_ptr<Promise<size_t>> promise(io_write->promise);
+      set_io_promise(
+          promise.get(), static_cast<size_t>(bytes_transferred), error);
+      return;
+    }
+    case IOType::CONNECT: {
+      IOOverlappedConnect* io_connect =
+        CONTAINING_RECORD(overlapped_base, IOOverlappedConnect, base);
+
+      std::unique_ptr<Promise<Nothing>> promise(io_connect->promise);
+      set_io_promise(promise.get(), Nothing(), error);
+      return;
+    }
+    case IOType::ACCEPT: {
+      IOOverlappedAccept* io_accept =
+        CONTAINING_RECORD(overlapped_base, IOOverlappedAccept, base);
+
+      std::unique_ptr<Promise<Nothing>> promise(io_accept->promise);
+      set_io_promise(promise.get(), Nothing(), error);
+      return;
+    }
+  }
+
+  UNREACHABLE();
+}
+
+
+// Function to handle all IOCP notifications. Returns true if the IOCP loop
+// should continue and false if it should stop.
+static bool check_and_handle_completion(const OVERLAPPED_ENTRY& entry)
+{
+  switch (entry.lpCompletionKey) {
+    case KEY_QUIT: {
+      return false;
+    }
+    case KEY_TIMER: {
+      // In the IOCP, we just set the timer. When the timer completes, it will
+      // queue up an APC that will interrupt the IOCP loop in order to execute
+      // the APC callback.
+      TimerOverlapped* timer =
+        reinterpret_cast<TimerOverlapped*>(entry.lpOverlapped);
+
+      const BOOL success = ::SetWaitableTimer(
+          timer->timer, &timer->time, 0, &timer_apc, timer, TRUE);
+
+      if (!success) {
+        // Nothing we can really do here aside from log the event.
+        std::string errorMsg = WindowsError().message;
+        LOG(FATAL)
+          << "process::windows::handle_completion failed to set timer: "
+          << errorMsg;
+      }
+      return true;
+    }
+    case KEY_IO: {
+      if (entry.lpOverlapped == nullptr) {
+        // Don't know if this is possible, but just log it in case.
+        LOG(FATAL) << "process::windows::handle_completion returned with a null"
+                   << "overlapped object";
+      } else {
+        handle_io(entry.lpOverlapped, entry.dwNumberOfBytesTransferred);
+      }
+      return true;
+    }
+  }
+
+  UNREACHABLE();
+}
+
+
+// Windows Event loop implementation.
+Try<EventLoop*> EventLoop::create()
+{
+  HANDLE iocp_handle =
+    ::CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 1);
+
+  if (iocp_handle == nullptr) {
+    return WindowsError();
+  }
+
+  return new EventLoop(iocp_handle);
+}
+
+EventLoop::EventLoop(HANDLE iocp_handle)
+  : iocp_handle_(std::unique_ptr<HANDLE, HandleDeleter>(iocp_handle))
+{}
+
+Try<Nothing> EventLoop::run()
+{
+  const Try<long> maxEntries = os::cpus();
+  if (maxEntries.isError()) {
+    return Error(maxEntries.error());
+  }
+
+  bool loop = true;
+  std::vector<OVERLAPPED_ENTRY> entries(maxEntries.get());
+  while (loop) {
+    ULONG dequeued_entries;
+
+    // This function can return in three ways:
+    //   1) We get some IO completion events and the function will return true.
+    //   2) A timer APC interrupts the function in its alertable wait status,
+    //      so the thread will execute the APC. The function will return false
+    //      and set the error to `WAIT_IO_COMPLETION`.
+    //   3) We get a legitimate error, so we exit early.
+    BOOL success = ::GetQueuedCompletionStatusEx(
+        iocp_handle_.get(),
+        entries.data(),
+        maxEntries.get(),
+        &dequeued_entries,
+        INFINITE,
+        TRUE);
+
+    if (!success) {
+      // Case 2: Got APC interrupt. We simply continue the loop.
+      if (::GetLastError() == WAIT_IO_COMPLETION) {
+        continue;
+      }
+
+      // We hit case 3, which means we got a serious error.
+      return WindowsError();
+    }
+
+    // Case 1: Dequeue completion packets and process them. If we get a quit
+    // notification, then we will finish the current queue and then exit.
+    for (ULONG i = 0; i < dequeued_entries; i++) {
+      const bool continue_loop = check_and_handle_completion(entries[i]);
+      loop = loop && continue_loop;
+    }
+  }
+  return Nothing();
+}
+
+
+Try<Nothing> EventLoop::stop()
+{
+  const BOOL success =
+    ::PostQueuedCompletionStatus(iocp_handle_.get(), 0, KEY_QUIT, nullptr);
+
+  if (success) {
+    return Nothing();
+  }
+
+  return WindowsError();
+}
+
+
+Try<Nothing> EventLoop::launchTimer(
+    const Duration& duration, const lambda::function<void()>& callback)
+{
+  // Create a non-inheritable, manual reset, unnamed timer.
+  HANDLE timer = ::CreateWaitableTimerW(nullptr, true, nullptr);
+  if (timer == nullptr) {
+    return WindowsError();
+  }
+
+  // If you give a positive value, then the timer call interprets it as
+  // absolute time. A negative value is interpretted as relative time.
+  // 0 is run immediately. The resolution is in 100ns.
+  LARGE_INTEGER time_elapsed;
+  time_elapsed.QuadPart = -duration.ns() / 100;
+
+  TimerOverlapped* overlapped =
+    new TimerOverlapped{timer, time_elapsed, callback};
+
+  // We don't actually set the timer here since APCs only execute in the same
+  // thread that called the async function. So, we queue the function call to
+  // the IOCP so the event loop thread can queue the APC.
+  //
+  // NOTE: `::PostQueuedCompletionStatus` does not process the second to fourth
+  // arguments, so you can give anything for them. Specifically, the overlapped
+  // parameter doesn't need to point to a `OVERLAPPED` structure. See
+  // https://msdn.microsoft.com/en-us/library/windows/desktop/aa365458(v=vs.85).aspx // NOLINT(whitespace/line_length)
+  const BOOL success = ::PostQueuedCompletionStatus(
+      iocp_handle_.get(),
+      0,
+      KEY_TIMER,
+      reinterpret_cast<OVERLAPPED*>(overlapped));
+
+  if (!success) {
+    // Failing `PostQueuedCompletionStatus` means we have to clean the
+    // memory here, since the APC won't execute.
+    WindowsError error;
+    delete overlapped;
+    ::CloseHandle(timer);
+    return error;
+  }
+
+  // The APC callback will clean up the memory and handle, so we can return.
+  return Nothing();
+}
+
+
+// NOTE: The following functions use `int_fd` instead of the native Win32
+// `HANDLE`, because they are the more "public" libwinio APIs that interface
+// directly with the libprocess IO, socket and event loop code.
+Try<Nothing> EventLoop::registerHandle(const int_fd& fd)
+{
+  Try<HANDLE> assigned_handle = fd.assign_iocp(iocp_handle_.get(), KEY_IO);
+  if (assigned_handle.isError()) {
+    return Error(assigned_handle.error());
+  }
+
+  // In this case, the IOCP handle was already assigned. So, we check if the
+  // right one is assigned.
+  if (assigned_handle.get() != nullptr) {
+    if (assigned_handle.get() != iocp_handle_.get()) {
+      return Error(
+          "fd is already registered to a different Windows Event Loop");
+    }
+    return Nothing();
+  }
+
+  // This is the first time the handle was assigned. We continue the
+  // initialization. These are some optimizations that prevent IOCP
+  // notifications on success and event notifications, so that we have less
+  // context switches.
+  BOOL success = ::SetFileCompletionNotificationModes(
+      fd, FILE_SKIP_COMPLETION_PORT_ON_SUCCESS | FILE_SKIP_SET_EVENT_ON_HANDLE);
+
+  if (!success) {
+    return WindowsError();
+  }
+
+  return Nothing();
+}
+
+
+template <typename T, typename O>
+static void enable_cancellation(
+    const int_fd& fd,
+    const Future<T>& future,
+    const std::shared_ptr<O>& overlapped)
+{
+  // We capture a `std::weak_ptr` in the `.onDiscard` callback, so that
+  // we can cancel the IO operation with the overlapped object if it still
+  // exists. The captured `shared_ptr` in the `.onAny` callback ensures that
+  // we keep the overlapped object alive until the IOCP callbacks are done.
+  auto overlapped_weak = std::weak_ptr<O>(overlapped);
+
+  future.onDiscard([fd, overlapped_weak]() {
+    std::shared_ptr<O> cancel = overlapped_weak.lock();
+    if (static_cast<bool>(cancel)) {
+      // We were able to get a reference to the overlapped object, so let's
+      // try to cancel the IO operation. Note that there is technically a
+      // race here. We could be between the IO completion event and deleting
+      // the overlapped object. In that case, this function will just no-op.
+      ::CancelIoEx(fd, &cancel->base.overlapped);
+    }
+  });
+
+  future.onAny([overlapped]() {});
+}
+
+
+static Future<size_t> read_internal(
+    const int_fd& fd, void* buf, size_t size, IOType type)
+{
+  process::initialize();
+
+  Promise<size_t>* promise = new Promise<size_t>();
+  Future<size_t> future = promise->future();
+
+  // We use a `std::shared_ptr`, so we can safely support canceling.
+  auto overlapped = std::make_shared<IOOverlappedReadWrite>(
+      IOOverlappedBase{OVERLAPPED{}, fd, type}, promise);
+
+  enable_cancellation(fd, future, overlapped);
+
+  // Start the asynchronous operation.
+  const Result<size_t> result =
+    os::read_async(fd, buf, size, &overlapped->base.overlapped);
+
+  // If the request is pending, then we return immediately and have the
+  // callback free the promise and overlapped.
+  if (result.isNone()) {
+    return future;
+  }
+
+  // In an error or immediate success, we have to manually set the promise
+  // and free it.
+  if (result.isError()) {
+    promise->fail("os::read_async failed: " + result.error());
+  } else if (result.isSome()) {
+    promise->set(result.get());
+  }
+  delete promise;
+  return future;
+}
+
+
+static Future<size_t> write_internal(
+    const int_fd& fd, const void* buf, size_t size, IOType type)
+{
+  process::initialize();
+
+  Promise<size_t>* promise = new Promise<size_t>();
+  Future<size_t> future = promise->future();
+
+  // We use a `std::shared_ptr`, so we can safely support canceling.
+  auto overlapped = std::make_shared<IOOverlappedReadWrite>(
+      IOOverlappedBase{OVERLAPPED{}, fd, type}, promise);
+
+  enable_cancellation(fd, future, overlapped);
+
+  // Start the asynchronous operation.
+  const Result<size_t> result =
+    os::write_async(fd, buf, size, &overlapped->base.overlapped);
+
+  // If the request is pending, then we return immediately and have the
+  // callback free the promise and overlapped.
+  if (result.isNone()) {
+    return future;
+  }
+
+  // In an error or immediate success, we have to manually set the promise
+  // and free it.
+  if (result.isError()) {
+    promise->fail("os::write_async failed: " + result.error());
+  } else if (result.isSome()) {
+    promise->set(result.get());
+  }
+  delete promise;
+  return future;
+}
+
+
+Future<size_t> read(const int_fd& fd, void* buf, size_t size)
+{
+  return read_internal(fd, buf, size, IOType::READ);
+}
+
+
+Future<size_t> write(const int_fd& fd, const void* buf, size_t size)
+{
+  return write_internal(fd, buf, size, IOType::WRITE);
+}
+
+
+Future<size_t> recv(const int_fd& fd, void* buf, size_t size)
+{
+  return read_internal(fd, buf, size, IOType::RECV);
+}
+
+
+Future<size_t> send(const int_fd& fd, const void* buf, size_t size)
+{
+  return write_internal(fd, buf, size, IOType::SEND);
+}
+
+
+Future<Nothing> accept(const int_fd& fd, const int_fd& accepted_socket)
+{
+  process::initialize();
+
+  Promise<Nothing>* promise = new Promise<Nothing>();
+  Future<Nothing> future = promise->future();
+
+  // We use a `std::shared_ptr`, so we can safely support canceling.
+  auto overlapped = std::make_shared<IOOverlappedAccept>(
+      IOOverlappedBase{OVERLAPPED{}, fd, IOType::ACCEPT}, promise);
+
+  enable_cancellation(fd, future, overlapped);
+
+  // The `overlapped->buf` passed into `::AcceptEx` will receive the first
+  // data block sent, the local address of the server and the remote address
+  // of the client. The (4th, 5th, 6th) arguments are
+  // (0, sizeof(buf)/2 , sizeof(buf) / 2), since we ignore the first block,
+  // and simply store the local and remote addresses. For more details, see
+  // https://msdn.microsoft.com/en-us/library/windows/desktop/ms737524(v=vs.85).aspx // NOLINT(whitespace/line_length)
+  DWORD bytes;
+  const BOOL success = ::AcceptEx(
+      fd,
+      accepted_socket,
+      overlapped->buf,
+      0,
+      sizeof(overlapped->buf) / 2,
+      sizeof(overlapped->buf) / 2,
+      &bytes,
+      &overlapped->base.overlapped);
+
+  const DWORD error = ::WSAGetLastError();
+
+  // If the request is pending, then we return immediately and have the
+  // callback free the promise and overlapped
+  if (!success && error == WSA_IO_PENDING) {
+    return future;
+  }
+
+  // In an error or immediate success, we have to manually set the promise
+  // and free it.
+  if (success) {
+    promise->set(Nothing());
+  } else {
+    promise->fail("AcceptEx failed: " + WindowsError(error).message);
+  }
+  delete promise;
+  return future;
+}
+
+// The MSDN docs state that `::ConnectEx` must be retrieved through
+// `::WSAIoctl`. See the remarks section of the docs:
+// https://msdn.microsoft.com/en-us/library/windows/desktop/ms737606(v=vs.85).aspx // NOLINT(whitespace/line_length)
+static LPFN_CONNECTEX init_connect_ex(const int_fd& fd)
+{
+  LPFN_CONNECTEX connect_ex;
+  GUID connect_ex_guid = WSAID_CONNECTEX;
+  DWORD bytes;
+
+  int res = ::WSAIoctl(
+      fd,
+      SIO_GET_EXTENSION_FUNCTION_POINTER,
+      &connect_ex_guid,
+      sizeof(connect_ex_guid),
+      &connect_ex,
+      sizeof(connect_ex),
+      &bytes,
+      nullptr,
+      nullptr);
+
+  // We can't use connect if we failed to get the function, so we just force
+  // an abort.
+  CHECK_EQ(res, 0);
+  return connect_ex;
+}
+
+
+static LPFN_CONNECTEX& get_connect_ex_ptr(const int_fd& fd)
+{
+  // C++11 magic static initialization.
+  static LPFN_CONNECTEX ptr = init_connect_ex(fd);
+  return ptr;
+}
+
+
+Future<Nothing> connect(const int_fd& fd, const network::Address& address)
+{
+  process::initialize();
+
+  // `::ConnectEx` needs the socket to be bound first.
+  Try<Nothing> bind_result = Nothing();
+  if (address.family() == network::Address::Family::INET4) {
+    const network::inet4::Address addr = network::inet4::Address::ANY_ANY();
+    bind_result = network::bind(fd, addr);
+  } else if (address.family() == network::Address::Family::INET6) {
+    const network::inet6::Address addr = network::inet6::Address::ANY_ANY();
+    bind_result = network::bind(fd, addr);
+  } else {
+    return Failure("Async connect only supports IPv6 and IPv4");
+  }
+
+  if (bind_result.isError()) {
+    // `WSAEINVAL` means socket is already bound, so we can continue. If it was
+    // bound incorrectly, then we can get an error later on.
+    if (::WSAGetLastError() != WSAEINVAL) {
+      return Failure("Failed to bind connect socket: " + bind_result.error());
+    }
+  }
+
+  // Load `::ConnectEx` function pointer, since it's not normally available.
+  const sockaddr_storage storage = address;
+  const int address_size = static_cast<int>(address.size());
+  LPFN_CONNECTEX connect_ex = get_connect_ex_ptr(fd);
+
+  Promise<Nothing>* promise = new Promise<Nothing>();
+  Future<Nothing> future = promise->future();
+
+  auto overlapped = std::make_shared<IOOverlappedConnect>(
+      IOOverlappedBase{OVERLAPPED{}, fd, IOType::CONNECT}, promise);
+
+  enable_cancellation(fd, future, overlapped);
+
+  const BOOL success = connect_ex(
+      fd,
+      reinterpret_cast<const sockaddr*>(&storage),
+      address_size,
+      nullptr,
+      0,
+      nullptr,
+      &overlapped->base.overlapped);
+
+  const DWORD error = ::WSAGetLastError();
+
+  // If the request is pending, then we return immediately and have the
+  // callback free the promise and overlapped
+  if (!success && error == WSA_IO_PENDING) {
+    return future;
+  }
+
+  // In an error or immediate success, we have to manually set the promise
+  // and free it.
+  if (success) {
+    promise->set(Nothing());
+  } else {
+    promise->fail("AcceptEx failed: " + stringify(error));
+  }
+  delete promise;
+  return future;
+}
+
+
+Future<size_t> sendfile(
+    const int_fd& socket, const int_fd& file, off_t offset, size_t size)
+{
+  process::initialize();
+
+  if (offset < 0) {
+    return Failure("process::windows::sendfile got negative offset");
+  }
+
+  Promise<size_t>* promise = new Promise<size_t>();
+  Future<size_t> future = promise->future();
+
+  auto overlapped = std::make_shared<IOOverlappedReadWrite>(
+      IOOverlappedBase{OVERLAPPED{}, socket, IOType::SENDFILE}, promise);
+
+  uint64_t offset64 = static_cast<uint64_t>(offset);
+  overlapped->base.overlapped.Offset = static_cast<DWORD>(offset64);
+  overlapped->base.overlapped.OffsetHigh = static_cast<DWORD>(offset64 >> 32);
+
+  enable_cancellation(socket, future, overlapped);
+
+  const Result<size_t> result =
+    os::sendfile_async(socket, file, size, &overlapped->base.overlapped);
+
+  // If the request is pending, then we return immediately and have the
+  // callback free the promise and overlapped.
+  if (result.isNone()) {
+    return future;
+  }
+
+  // In an error or immediate success, we have to manually set the promise
+  // and free it.
+  if (result.isError()) {
+    promise->fail("os::sendfile_async failed: " + result.error());
+  } else if (result.isSome()) {
+    promise->set(result.get());
+  }
+  delete promise;
+  return future;
+}
+
+} // namespace windows {
+} // namespace process {

http://git-wip-us.apache.org/repos/asf/mesos/blob/bfeda6b5/3rdparty/libprocess/src/windows/libwinio.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/windows/libwinio.hpp b/3rdparty/libprocess/src/windows/libwinio.hpp
new file mode 100644
index 0000000..74f1939
--- /dev/null
+++ b/3rdparty/libprocess/src/windows/libwinio.hpp
@@ -0,0 +1,85 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#ifndef __LIBWINIO_HPP__
+#define __LIBWINIO_HPP__
+
+#include <stout/lambda.hpp>
+#include <stout/try.hpp>
+#include <stout/windows.hpp>
+
+#include <stout/os/int_fd.hpp>
+
+#include <process/address.hpp>
+#include <process/future.hpp>
+
+namespace process {
+namespace windows {
+
+class EventLoop
+{
+public:
+  static Try<EventLoop*> create();
+
+  // Run the event loop forever.
+  Try<Nothing> run();
+
+  // Signal the event loop to stop.
+  Try<Nothing> stop();
+
+  // Timer event that calls the callback after the timer has fired.
+  Try<Nothing> launchTimer(
+      const Duration& duration, const lambda::function<void()>& callback);
+
+  // Register a handle for async IO.
+  Try<Nothing> registerHandle(const int_fd& fd);
+
+private:
+  EventLoop(HANDLE iocp_handle);
+
+  // Custom Deleter for a RAII handle.
+  struct HandleDeleter
+  {
+    // `HANDLE` satisfies the C++ `NullablePointer` interface, since it's
+    // a literal pointer. So we can use the `HANDLE` directly instead of
+    // using a pointer to the `HANDLE`.
+    // See http://en.cppreference.com/w/cpp/memory/unique_ptr for more info.
+    typedef HANDLE pointer;
+    void operator()(HANDLE h) { ::CloseHandle(h); }
+  };
+
+  std::unique_ptr<HANDLE, HandleDeleter> iocp_handle_;
+};
+
+
+// All of these functions do an asynchronous IO operation. The returned future
+// can be discarded to cancel the operation.
+Future<size_t> read(const int_fd& fd, void* buf, size_t size);
+
+Future<size_t> write(const int_fd& fd, const void* buf, size_t size);
+
+// Socket only functions.
+Future<size_t> recv(const int_fd& fd, void* buf, size_t size);
+
+Future<size_t> send(const int_fd& fd, const void* buf, size_t size);
+
+Future<Nothing> accept(const int_fd& fd, const int_fd& accepted_socket);
+
+Future<Nothing> connect(const int_fd& fd, const network::Address& address);
+
+Future<size_t> sendfile(
+    const int_fd& fd, const int_fd& file_fd, off_t offset, size_t size);
+
+} // namespace windows {
+} // namespace process {
+
+#endif // __LIBWINIO_HPP__


[13/16] mesos git commit: Windows: Added `windows_to_unix_epoch` function.

Posted by an...@apache.org.
Windows: Added `windows_to_unix_epoch` function.

There are a couple of places where we need to convert a Windows
absolute time (epoch at 01/01/1601) to a UNIX absolute time (epoch at
01/01/1970). So, a function has been added to handle it.

Review: https://reviews.apache.org/r/67685/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/184338e0
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/184338e0
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/184338e0

Branch: refs/heads/master
Commit: 184338e0992c3599a53e8e481a89a5b73e543e79
Parents: bfeda6b
Author: Akash Gupta <ak...@hotmail.com>
Authored: Wed Jun 27 14:30:15 2018 -0700
Committer: Andrew Schwartzmeyer <an...@schwartzmeyer.com>
Committed: Wed Jun 27 15:06:10 2018 -0700

----------------------------------------------------------------------
 .../stout/include/stout/os/windows/stat.hpp     | 25 +++-------------
 3rdparty/stout/include/stout/windows/os.hpp     | 30 ++++++++++++++++++++
 2 files changed, 34 insertions(+), 21 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/184338e0/3rdparty/stout/include/stout/os/windows/stat.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/stout/include/stout/os/windows/stat.hpp b/3rdparty/stout/include/stout/os/windows/stat.hpp
index 7838bac..6225609 100644
--- a/3rdparty/stout/include/stout/os/windows/stat.hpp
+++ b/3rdparty/stout/include/stout/os/windows/stat.hpp
@@ -23,6 +23,8 @@
 
 #include <stout/os/int_fd.hpp>
 
+#include <stout/windows/os.hpp>
+
 #include <stout/internal/windows/attributes.hpp>
 #include <stout/internal/windows/longpath.hpp>
 #include <stout/internal/windows/reparsepoint.hpp>
@@ -166,27 +168,8 @@ inline Try<long> mtime(
     return WindowsError();
   }
 
-  // Convert to 64-bit integer using Windows magic.
-  ULARGE_INTEGER largetime;
-  largetime.LowPart = filetime.dwLowDateTime;
-  largetime.HighPart = filetime.dwHighDateTime;
-  // Now the `QuadPart` field is the 64-bit representation due to the
-  // layout of the `ULARGE_INTEGER` struct.
-  static_assert(
-      sizeof(largetime.QuadPart) == sizeof(__int64),
-      "Expected `QuadPart` to be of type `__int64`");
-  const __int64 windowstime = largetime.QuadPart;
-  // A file time is a 64-bit value that represents the number of
-  // 100-nanosecond intervals that have elapsed since 1601-01-01
-  // 00:00:00 +0000. However, users of this function expect UNIX time,
-  // which is seconds elapsed since the Epoch, 1970-01-01 00:00:00
-  // +0000.
-  //
-  // So first we convert 100-nanosecond intervals into seconds by
-  // doing `(x * 100) / (1,000^3)`, or `x / 10,000,000`, and then
-  // substracting the number of seconds between 1601-01-01 and
-  // 1970-01-01, or `11,644,473,600`.
-  const __int64 unixtime = (windowstime / 10000000) - 11644473600;
+  const uint64_t unixtime = os::internal::windows_to_unix_epoch(filetime);
+
   // We choose to make this conversion explicit because we expect the
   // truncation to not cause information loss.
   return static_cast<long>(unixtime);

http://git-wip-us.apache.org/repos/asf/mesos/blob/184338e0/3rdparty/stout/include/stout/windows/os.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/stout/include/stout/windows/os.hpp b/3rdparty/stout/include/stout/windows/os.hpp
index 46cd667..4f26806 100644
--- a/3rdparty/stout/include/stout/windows/os.hpp
+++ b/3rdparty/stout/include/stout/windows/os.hpp
@@ -80,6 +80,36 @@ inline Try<std::string> nodename()
   return stringify(std::wstring(buffer.data()));
 }
 
+
+// Converts a `FILETIME` from an absoute Windows time, which is the number of
+// 100-nanosecond intervals since 1601-01-01 00:00:00 +0000, to an UNIX
+// absolute time, which is number of seconds from 1970-01-01 00:00:00 +0000.
+inline double windows_to_unix_epoch(const FILETIME& filetime)
+{
+  ULARGE_INTEGER time;
+
+  // `FILETIME` isn't 8 byte aligned so they suggest not do cast to int64*.
+  time.HighPart = filetime.dwHighDateTime;
+  time.LowPart = filetime.dwLowDateTime;
+
+  // Constant taken from here:
+  // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724228(v=vs.85).aspx. // NOLINT(whitespace/line_length)
+  // It is the number of 100ns periods between the Windows and UNIX epochs.
+  constexpr uint64_t epoch_offset = 116444736000000000ULL;
+
+  // Now the `QuadPart` field is the 64-bit representation due to the
+  // layout of the `ULARGE_INTEGER` struct.
+  static_assert(
+      std::is_same<decltype(time.QuadPart), uint64_t>::value,
+      "Expected `ULARGE_INTEGER.QuadPart` to be of type `uint64_t`");
+
+  CHECK_GE(time.QuadPart, epoch_offset)
+    << "windows_to_unix_epoch: Given time was before UNIX epoch: "
+    << time.QuadPart;
+
+  return static_cast<double>(time.QuadPart - epoch_offset) / 10000000;
+}
+
 } // namespace internal {
 
 


[15/16] mesos git commit: Windows: Wrapped docker executor code in job object.

Posted by an...@apache.org.
Windows: Wrapped docker executor code in job object.

One of the docker tests revealed that the docker executor code could
leak some of the docker processes if it gets killed. To avoid this, we
run the docker executor and all processes launched by it in a job
object, so that `os::killtree` will properly kill the process tree.

Review: https://reviews.apache.org/r/67456/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/1f4ead9c
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/1f4ead9c
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/1f4ead9c

Branch: refs/heads/master
Commit: 1f4ead9c1bba859ad1498d737a176b359268575d
Parents: 11f8b6d
Author: Akash Gupta <ak...@hotmail.com>
Authored: Wed Jun 27 14:30:19 2018 -0700
Committer: Andrew Schwartzmeyer <an...@schwartzmeyer.com>
Committed: Wed Jun 27 15:06:10 2018 -0700

----------------------------------------------------------------------
 src/docker/docker.cpp              | 65 ++++++++++++++++++++++++++++-----
 src/docker/executor.cpp            | 32 ++++++++++++++++
 src/slave/containerizer/docker.cpp | 15 ++++++++
 3 files changed, 103 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/1f4ead9c/src/docker/docker.cpp
----------------------------------------------------------------------
diff --git a/src/docker/docker.cpp b/src/docker/docker.cpp
index c6db244..baac70f 100644
--- a/src/docker/docker.cpp
+++ b/src/docker/docker.cpp
@@ -34,10 +34,18 @@
 #include <stout/os/read.hpp>
 #include <stout/os/write.hpp>
 
+#ifdef __WINDOWS__
+#include <stout/os/windows/jobobject.hpp>
+#endif // __WINDOWS__
+
 #include <process/check.hpp>
 #include <process/collect.hpp>
 #include <process/io.hpp>
 
+#ifdef __WINDOWS__
+#include <process/windows/jobobject.hpp>
+#endif // __WINDOWS__
+
 #include "common/status_utils.hpp"
 
 #include "docker/docker.hpp"
@@ -159,6 +167,19 @@ void commandDiscarded(const Subprocess& s, const string& cmd)
 }
 
 
+vector<Subprocess::ParentHook> createParentHooks()
+{
+  return {
+#ifdef __WINDOWS__
+  // To correctly discard the docker cli process tree in `commandDiscarded`,
+  // we need to wrap the process in a job object.
+  Subprocess::ParentHook::CREATE_JOB(),
+  Subprocess::ParentHook(&os::set_job_kill_on_close_limit),
+#endif // __WINDOWS__
+  };
+}
+
+
 Future<Version> Docker::version() const
 {
   string cmd = path + " -H " + socket + " --version";
@@ -167,7 +188,10 @@ Future<Version> Docker::version() const
       cmd,
       Subprocess::PATH(os::DEV_NULL),
       Subprocess::PIPE(),
-      Subprocess::PIPE());
+      Subprocess::PIPE(),
+      None(),
+      None(),
+      createParentHooks());
 
   if (s.isError()) {
     return Failure("Failed to create subprocess '" + cmd + "': " + s.error());
@@ -1143,7 +1167,10 @@ Future<Option<int>> Docker::run(
       Subprocess::PATH(os::DEV_NULL),
       _stdout,
       _stderr,
-      nullptr);
+      nullptr,
+      None(),
+      None(),
+      createParentHooks());
 
   if (s.isError()) {
     return Failure("Failed to create subprocess '" + path + "': " + s.error());
@@ -1186,7 +1213,10 @@ Future<Nothing> Docker::stop(
       cmd,
       Subprocess::PATH(os::DEV_NULL),
       Subprocess::PATH(os::DEV_NULL),
-      Subprocess::PIPE());
+      Subprocess::PIPE(),
+      None(),
+      None(),
+      createParentHooks());
 
   if (s.isError()) {
     return Failure("Failed to create subprocess '" + cmd + "': " + s.error());
@@ -1241,7 +1271,10 @@ Future<Nothing> Docker::kill(
       cmd,
       Subprocess::PATH(os::DEV_NULL),
       Subprocess::PATH(os::DEV_NULL),
-      Subprocess::PIPE());
+      Subprocess::PIPE(),
+      None(),
+      None(),
+      createParentHooks());
 
   if (s.isError()) {
     return Failure("Failed to create subprocess '" + cmd + "': " + s.error());
@@ -1266,7 +1299,10 @@ Future<Nothing> Docker::rm(
       cmd,
       Subprocess::PATH(os::DEV_NULL),
       Subprocess::PATH(os::DEV_NULL),
-      Subprocess::PIPE());
+      Subprocess::PIPE(),
+      None(),
+      None(),
+      createParentHooks());
 
   if (s.isError()) {
     return Failure("Failed to create subprocess '" + cmd + "': " + s.error());
@@ -1314,7 +1350,10 @@ void Docker::_inspect(
       cmd,
       Subprocess::PATH(os::DEV_NULL),
       Subprocess::PIPE(),
-      Subprocess::PIPE());
+      Subprocess::PIPE(),
+      None(),
+      None(),
+      createParentHooks());
 
   if (s.isError()) {
     promise->fail("Failed to create subprocess '" + cmd + "': " + s.error());
@@ -1451,7 +1490,10 @@ Future<vector<Docker::Container>> Docker::ps(
       cmd,
       Subprocess::PATH(os::DEV_NULL),
       Subprocess::PIPE(),
-      Subprocess::PIPE());
+      Subprocess::PIPE(),
+      None(),
+      None(),
+      createParentHooks());
 
   if (s.isError()) {
     return Failure("Failed to create subprocess '" + cmd + "': " + s.error());
@@ -1622,7 +1664,10 @@ Future<Docker::Image> Docker::pull(
       Subprocess::PATH(os::DEV_NULL),
       Subprocess::PIPE(),
       Subprocess::PIPE(),
-      nullptr);
+      nullptr,
+      None(),
+      None(),
+      createParentHooks());
 
   if (s.isError()) {
     return Failure("Failed to create subprocess '" + cmd + "': " + s.error());
@@ -1769,7 +1814,9 @@ Future<Docker::Image> Docker::__pull(
       Subprocess::PATH(os::DEV_NULL),
       Subprocess::PIPE(),
       nullptr,
-      environment);
+      environment,
+      None(),
+      createParentHooks());
 
   if (s_.isError()) {
     return Failure("Failed to execute '" + cmd + "': " + s_.error());

http://git-wip-us.apache.org/repos/asf/mesos/blob/1f4ead9c/src/docker/executor.cpp
----------------------------------------------------------------------
diff --git a/src/docker/executor.cpp b/src/docker/executor.cpp
index 572594a..d9f5a73 100644
--- a/src/docker/executor.cpp
+++ b/src/docker/executor.cpp
@@ -39,9 +39,16 @@
 #include <stout/os.hpp>
 #include <stout/protobuf.hpp>
 #include <stout/try.hpp>
+#ifdef __WINDOWS__
+#include <stout/windows.hpp>
+#endif // __WINDOWS__
 
 #include <stout/os/killtree.hpp>
 
+#ifdef __WINDOWS__
+#include <stout/os/windows/jobobject.hpp>
+#endif // __WINDOWS__
+
 #include "checks/checks_runtime.hpp"
 #include "checks/health_checker.hpp"
 
@@ -968,6 +975,31 @@ int main(int argc, char** argv)
 {
   GOOGLE_PROTOBUF_VERIFY_VERSION;
 
+#ifdef __WINDOWS__
+  // We need a handle to the job object which this container is associated with.
+  // Without this handle, the job object would be destroyed by the OS when the
+  // agent exits (or crashes), making recovery impossible. By holding a handle,
+  // we tie the lifetime of the job object to the container itself. In this way,
+  // a recovering agent can reattach to the container by opening a new handle to
+  // the job object.
+  const pid_t pid = ::GetCurrentProcessId();
+  const Try<std::wstring> name = os::name_job(pid);
+  if (name.isError()) {
+    cerr << "Failed to create job object name from pid: " << name.error()
+         << endl;
+    return EXIT_FAILURE;
+  }
+
+  // NOTE: This handle will not be destructed, even though it is a
+  // `SharedHandle`, because it will (purposefully) never go out of scope.
+  Try<SharedHandle> handle = os::open_job(JOB_OBJECT_QUERY, false, name.get());
+  if (handle.isError()) {
+    cerr << "Failed to open job object '" << stringify(name.get())
+         << "' for the current container: " << handle.error() << endl;
+    return EXIT_FAILURE;
+  }
+#endif // __WINDOWS__
+
   mesos::internal::docker::Flags flags;
 
   // Load flags from environment and command line.

http://git-wip-us.apache.org/repos/asf/mesos/blob/1f4ead9c/src/slave/containerizer/docker.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/docker.cpp b/src/slave/containerizer/docker.cpp
index 7737107..277a155 100644
--- a/src/slave/containerizer/docker.cpp
+++ b/src/slave/containerizer/docker.cpp
@@ -33,6 +33,10 @@
 #include <process/reap.hpp>
 #include <process/subprocess.hpp>
 
+#ifdef __WINDOWS__
+#include <process/windows/jobobject.hpp>
+#endif // __WINDOWS__
+
 #include <stout/adaptor.hpp>
 #include <stout/fs.hpp>
 #include <stout/hashmap.hpp>
@@ -46,6 +50,10 @@
 #include <stout/os/killtree.hpp>
 #include <stout/os/which.hpp>
 
+#ifdef __WINDOWS__
+#include <stout/os/windows/jobobject.hpp>
+#endif // __WINDOWS__
+
 #include "common/status_utils.hpp"
 
 #include "hook/manager.hpp"
@@ -1588,6 +1596,13 @@ Future<pid_t> DockerContainerizerProcess::launchExecutorProcess(
       parentHooks.emplace_back(Subprocess::ParentHook(
           &systemd::mesos::extendLifetime));
     }
+#elif __WINDOWS__
+    parentHooks.emplace_back(Subprocess::ParentHook::CREATE_JOB());
+    // Setting the "kill on close" job object limit ties the lifetime of the
+    // docker processes to that of the executor. This ensures that if the
+    // executor exits, the docker processes aren't leaked.
+    parentHooks.emplace_back(Subprocess::ParentHook(
+        [](pid_t pid) { return os::set_job_kill_on_close_limit(pid); }));
 #endif // __linux__
 
     // Prepare the flags to pass to the mesos docker executor process.


[05/16] mesos git commit: Windows: Added IOCP `HANDLE` to `WindowsFD`.

Posted by an...@apache.org.
Windows: Added IOCP `HANDLE` to `WindowsFD`.

Users of `os::nonblock()` assume that it is idempotent. Unfortunately,
`CreateIoCompletionPort` is not idempotent, so we need to wrap the
function around our own code to make it so. We need to keep it inside
the `WindowsFD` class, because there isn't a way to determine if a
`HANDLE` is associated with an IOCP through the Win32 API.

Review: https://reviews.apache.org/r/67385/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/2310609c
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/2310609c
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/2310609c

Branch: refs/heads/master
Commit: 2310609c49cc3ce30e2c9507bdf6c9ba18cc4bf1
Parents: f7f1c45
Author: Akash Gupta <ak...@hotmail.com>
Authored: Wed Jun 27 14:30:11 2018 -0700
Committer: Andrew Schwartzmeyer <an...@schwartzmeyer.com>
Committed: Wed Jun 27 15:06:10 2018 -0700

----------------------------------------------------------------------
 3rdparty/stout/include/stout/os/windows/dup.hpp | 13 +++-
 3rdparty/stout/include/stout/os/windows/fd.hpp  | 72 +++++++++++++++++++-
 2 files changed, 81 insertions(+), 4 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/2310609c/3rdparty/stout/include/stout/os/windows/dup.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/stout/include/stout/os/windows/dup.hpp b/3rdparty/stout/include/stout/os/windows/dup.hpp
index 5bda095..9dc2fa0 100644
--- a/3rdparty/stout/include/stout/os/windows/dup.hpp
+++ b/3rdparty/stout/include/stout/os/windows/dup.hpp
@@ -40,7 +40,9 @@ inline Try<int_fd> dup(const int_fd& fd)
         return WindowsError();
       }
 
-      return int_fd(duplicate, fd.is_overlapped());
+      WindowsFD dup_fd(fd);
+      dup_fd.handle_ = duplicate;
+      return dup_fd;
     }
     case WindowsFD::Type::SOCKET: {
       WSAPROTOCOL_INFOW info;
@@ -50,7 +52,14 @@ inline Try<int_fd> dup(const int_fd& fd)
         return SocketError();
       }
 
-      return ::WSASocketW(0, 0, 0, &info, 0, 0);
+      SOCKET duplicate = ::WSASocketW(0, 0, 0, &info, 0, 0);
+      if (duplicate == INVALID_SOCKET) {
+        return WindowsSocketError();
+      }
+
+      WindowsFD dup_fd(fd);
+      dup_fd.socket_ = duplicate;
+      return dup_fd;
     }
   }
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/2310609c/3rdparty/stout/include/stout/os/windows/fd.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/stout/include/stout/os/windows/fd.hpp b/3rdparty/stout/include/stout/os/windows/fd.hpp
index b472658..5c726fb 100644
--- a/3rdparty/stout/include/stout/os/windows/fd.hpp
+++ b/3rdparty/stout/include/stout/os/windows/fd.hpp
@@ -17,12 +17,14 @@
 #include <io.h> // For `_open_osfhandle`.
 
 #include <array>
+#include <atomic>
 #include <memory>
 #include <ostream>
 #include <type_traits>
 
 #include <stout/check.hpp>
 #include <stout/nothing.hpp>
+#include <stout/synchronized.hpp>
 #include <stout/try.hpp>
 #include <stout/unreachable.hpp>
 #include <stout/windows.hpp> // For `WinSock2.h`.
@@ -69,7 +71,10 @@ public:
       std::is_same<HANDLE, void*>::value,
       "Expected `HANDLE` to be of type `void*`.");
   explicit WindowsFD(HANDLE handle, bool overlapped = false)
-    : type_(Type::HANDLE), handle_(handle), overlapped_(overlapped)
+    : type_(Type::HANDLE),
+      handle_(handle),
+      overlapped_(overlapped),
+      iocp_(std::make_shared<IOCPData>())
   {}
 
   // The `SOCKET` here is expected to be Windows sockets, such as that
@@ -83,7 +88,10 @@ public:
       std::is_same<SOCKET, unsigned __int64>::value,
       "Expected `SOCKET` to be of type `unsigned __int64`.");
   explicit WindowsFD(SOCKET socket, bool overlapped = true)
-    : type_(Type::SOCKET), socket_(socket), overlapped_(overlapped)
+    : type_(Type::SOCKET),
+      socket_(socket),
+      overlapped_(overlapped),
+      iocp_(std::make_shared<IOCPData>())
   {}
 
   // On Windows, libevent's `evutil_socket_t` is set to `intptr_t`.
@@ -182,6 +190,49 @@ public:
 
   bool is_overlapped() const { return overlapped_; }
 
+  // Assigns this `WindowsFD` to an IOCP. Returns `nullptr` is this is
+  // the first time that `this` was assigned to an IOCP. If `this` was
+  // already assigned, then this function no-ops and returns the
+  // assigned IOCP `HANDLE`. We have this function because
+  // `CreateIoCompletionPort` returns an error if a `HANDLE` gets
+  // assigned to an IOCP `HANDLE` twice, but provides no way to check
+  // for that error.
+  //
+  // NOTE: The `key` parameter is `CompletionKey` in
+  // `CreateIoCompletionPort`. As stated by the MSDN docs, this key is
+  // the "per-handle user-defined completion key that is included in
+  // every I/O completion packet for the specified file handle" [1],
+  // and thus, it's only used for bookkeeping by the user and not used
+  // for functional control in `CreateIoCompletionPort`.
+  //
+  // [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa363862(v=vs.85).aspx // NOLINT(whitespace/line_length)
+  Try<HANDLE> assign_iocp(HANDLE target, ULONG_PTR key) const
+  {
+    synchronized (iocp_->lock) {
+      const HANDLE prev_handle = iocp_->handle;
+      if (prev_handle == nullptr) {
+        const HANDLE fd_handle =
+          type() == Type::SOCKET ? reinterpret_cast<HANDLE>(socket_) : handle_;
+
+        // Confusing name, but `::CreateIoCompletionPort` can also
+        // assign a `HANDLE` to an IO completion port.
+        if (::CreateIoCompletionPort(fd_handle, target, key, 0) == nullptr) {
+          return WindowsError();
+        }
+
+        iocp_->handle = target;
+      }
+      return prev_handle;
+    }
+  }
+
+  HANDLE get_iocp() const
+  {
+    synchronized (iocp_->lock) {
+      return iocp_->handle;
+    }
+  }
+
 private:
   Type type_;
 
@@ -193,6 +244,18 @@ private:
 
   bool overlapped_;
 
+  // There can be many `int_fd` copies of the same `HANDLE` and many `HANDLE`
+  // can reference the same kernel `FILE_OBJECT`. Since the IOCP affects the
+  // underlying `FILE_OBJECT`, we keep a pointer to the IOCP handle so we can
+  // update it for all int_fds that refer to the same `FILE_OBJECT`.
+  struct IOCPData
+  {
+    std::atomic_flag lock = ATOMIC_FLAG_INIT;
+    HANDLE handle = nullptr;
+  };
+
+  std::shared_ptr<IOCPData> iocp_;
+
   // NOTE: This function is provided only for checking validity, thus
   // it is private. It provides a view of a `WindowsFD` as an `int`.
   //
@@ -224,6 +287,11 @@ private:
   friend bool operator==(const WindowsFD& left, int right);
   friend bool operator!=(int left, const WindowsFD& right);
   friend bool operator!=(const WindowsFD& left, int right);
+
+  // `os::dup` needs to modify a `WindowsFD`'s private state, because
+  // when we want to `os::dup` a `WindowsFD`, we need to copy the IOCP
+  // handle and the overlapped state, but NOT the handle value itself.
+  friend Try<WindowsFD> dup(const WindowsFD& fd);
 };
 
 


[03/16] mesos git commit: Windows: Integrated libwinio with libprocess code.

Posted by an...@apache.org.
Windows: Integrated libwinio with libprocess code.

The libprocess event loops are used to implement the `EventLoop`,
`SocketImpl`, and `io::read/write` interfaces. Thus, by providing
those implementations using libwinio, libprocess now supports the
libwinio backend.

Review: https://reviews.apache.org/r/67390/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/5fd78dfe
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/5fd78dfe
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/5fd78dfe

Branch: refs/heads/master
Commit: 5fd78dfe162b8d8dff65d52feb44326de81472d2
Parents: 184338e
Author: Akash Gupta <ak...@hotmail.com>
Authored: Wed Jun 27 14:30:16 2018 -0700
Committer: Andrew Schwartzmeyer <an...@schwartzmeyer.com>
Committed: Wed Jun 27 15:06:10 2018 -0700

----------------------------------------------------------------------
 3rdparty/libprocess/src/windows/event_loop.cpp  |  68 ++++++
 3rdparty/libprocess/src/windows/event_loop.hpp  |  25 +++
 3rdparty/libprocess/src/windows/io.cpp          | 129 ++++++++++++
 3rdparty/libprocess/src/windows/poll_socket.cpp | 207 +++++++++++++++++++
 4 files changed, 429 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/5fd78dfe/3rdparty/libprocess/src/windows/event_loop.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/windows/event_loop.cpp b/3rdparty/libprocess/src/windows/event_loop.cpp
new file mode 100644
index 0000000..0050ff0
--- /dev/null
+++ b/3rdparty/libprocess/src/windows/event_loop.cpp
@@ -0,0 +1,68 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#include <event_loop.hpp>
+
+#include <process/logging.hpp>
+#include <process/once.hpp>
+
+#include <stout/windows.hpp>
+
+#include <stout/windows/os.hpp>
+
+#include "windows/event_loop.hpp"
+#include "windows/libwinio.hpp"
+
+namespace process {
+
+windows::EventLoop* libwinio_loop;
+
+void EventLoop::initialize()
+{
+  static Once* initialized = new Once();
+
+  if (initialized->once()) {
+    return;
+  }
+
+  Try<windows::EventLoop*> try_loop = windows::EventLoop::create();
+  if (try_loop.isError()) {
+    LOG(FATAL) << "Failed to initialize Windows IOCP event loop";
+  }
+
+  libwinio_loop = try_loop.get();
+
+  initialized->done();
+}
+
+
+void EventLoop::delay(
+    const Duration& duration, const std::function<void()>& function)
+{
+  libwinio_loop->launchTimer(duration, function);
+}
+
+
+double EventLoop::time()
+{
+  FILETIME filetime;
+  ::GetSystemTimeAsFileTime(&filetime);
+  return os::internal::windows_to_unix_epoch(filetime);
+}
+
+
+void EventLoop::run() { libwinio_loop->run(); }
+
+
+void EventLoop::stop() { libwinio_loop->stop(); }
+
+} // namespace process {

http://git-wip-us.apache.org/repos/asf/mesos/blob/5fd78dfe/3rdparty/libprocess/src/windows/event_loop.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/windows/event_loop.hpp b/3rdparty/libprocess/src/windows/event_loop.hpp
new file mode 100644
index 0000000..945b876
--- /dev/null
+++ b/3rdparty/libprocess/src/windows/event_loop.hpp
@@ -0,0 +1,25 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#ifndef __WINDOWS_EVENT_LOOP_HPP__
+#define __WINDOWS_EVENT_LOOP_HPP__
+
+#include "windows/libwinio.hpp"
+
+namespace process {
+
+// Windows event loop variable.
+extern windows::EventLoop* libwinio_loop;
+
+} // namespace process {
+
+#endif // __WINDOWS_EVENT_LOOP_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/5fd78dfe/3rdparty/libprocess/src/windows/io.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/windows/io.cpp b/3rdparty/libprocess/src/windows/io.cpp
new file mode 100644
index 0000000..1f9adde
--- /dev/null
+++ b/3rdparty/libprocess/src/windows/io.cpp
@@ -0,0 +1,129 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#include <string>
+
+#include <process/future.hpp>
+#include <process/process.hpp> // For process::initialize.
+
+#include <stout/error.hpp>
+#include <stout/try.hpp>
+#include <stout/windows.hpp>
+
+#include <stout/os/read.hpp>
+#include <stout/os/write.hpp>
+
+#include "io_internal.hpp"
+
+#include "windows/libwinio.hpp"
+#include "windows/event_loop.hpp"
+
+namespace process {
+namespace io {
+namespace internal {
+
+Future<size_t> read(int_fd fd, void* data, size_t size)
+{
+  process::initialize();
+
+  // TODO(benh): Let the system calls do what ever they're supposed to
+  // rather than return 0 here?
+  if (size == 0) {
+    return 0;
+  }
+
+  // Just do a synchronous call.
+  if (!fd.is_overlapped()) {
+    ssize_t result = os::read(fd, data, size);
+    if (result == -1) {
+      return Failure(WindowsError().message);
+    }
+    return static_cast<size_t>(result);
+  }
+
+  return windows::read(fd, data, size);
+}
+
+Future<size_t> write(int_fd fd, const void* data, size_t size)
+{
+  process::initialize();
+
+  // TODO(benh): Let the system calls do what ever they're supposed to
+  // rather than return 0 here?
+  if (size == 0) {
+    return 0;
+  }
+
+  // Just do a synchronous call.
+  if (!fd.is_overlapped()) {
+    ssize_t result = os::write(fd, data, size);
+    if (result == -1) {
+      return Failure(WindowsError().message);
+    }
+    return static_cast<size_t>(result);
+  }
+
+  return windows::write(fd, data, size);
+}
+
+
+Try<Nothing> prepare_async(int_fd fd)
+{
+  if (fd.is_overlapped()) {
+    return libwinio_loop->registerHandle(fd);
+  }
+
+  // For non-overlapped fds, we will only error if it's not a disk file, since
+  // those are "fast" devices, so blocking IO should be okay. This is the same
+  // as Linux's `O_NONBLOCK`, which doesn't work on regular files.
+  if (fd.type() == os::WindowsFD::Type::SOCKET) {
+    return Error("Got a non-overlapped socket");
+  }
+
+  DWORD type = ::GetFileType(fd);
+  if (type != FILE_TYPE_DISK) {
+    WindowsError error;
+    return Error(std::string(
+        "io::prepare_async only accepts disk files for non-overlapped files. "
+        "Got type: ") + stringify(type) + " with possible error: " +
+        error.message);
+  }
+
+  return Nothing();
+}
+
+
+Try<bool> is_async(int_fd fd)
+{
+  if (fd.is_overlapped()) {
+    return fd.get_iocp() != nullptr;
+  }
+
+  // For non-overlapped fds, we only accept disk files, since they are "fast"
+  // devices, similar to how Linux `O_NONBLOCK` no-ops on "fast" file types
+  // like regular files.
+  if (fd.type() == os::WindowsFD::Type::SOCKET) {
+    return false;
+  }
+
+  DWORD type = ::GetFileType(fd);
+  WindowsError error;
+  if (type == FILE_TYPE_UNKNOWN && error.code != NO_ERROR) {
+    return error;
+  }
+
+  return type == FILE_TYPE_DISK;
+}
+
+} // namespace internal {
+} // namespace io {
+} // namespace process {

http://git-wip-us.apache.org/repos/asf/mesos/blob/5fd78dfe/3rdparty/libprocess/src/windows/poll_socket.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/windows/poll_socket.cpp b/3rdparty/libprocess/src/windows/poll_socket.cpp
new file mode 100644
index 0000000..565b008
--- /dev/null
+++ b/3rdparty/libprocess/src/windows/poll_socket.cpp
@@ -0,0 +1,207 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+
+#include <process/io.hpp>
+#include <process/loop.hpp>
+#include <process/network.hpp>
+#include <process/socket.hpp>
+
+#include <stout/windows.hpp>
+
+#include <stout/os.hpp>
+#include <stout/os/sendfile.hpp>
+#include <stout/os/strerror.hpp>
+
+#include "config.hpp"
+#include "poll_socket.hpp"
+
+#include "windows/libwinio.hpp"
+
+using std::string;
+
+namespace process {
+namespace network {
+namespace internal {
+
+Try<std::shared_ptr<SocketImpl>> PollSocketImpl::create(int_fd s)
+{
+  return std::make_shared<PollSocketImpl>(s);
+}
+
+
+Try<Nothing> PollSocketImpl::listen(int backlog)
+{
+  if (::listen(get(), backlog) < 0) {
+    return ErrnoError();
+  }
+  return Nothing();
+}
+
+
+Future<std::shared_ptr<SocketImpl>> PollSocketImpl::accept()
+{
+  // Need to hold a copy of `this` so that the underlying socket
+  // doesn't end up getting reused before we return from the call to
+  // `io::poll` and end up accepting a socket incorrectly.
+  auto self = shared(this);
+
+  Try<Address> address = network::address(get());
+  if (address.isError()) {
+    return Failure("Failed to get address: " + address.error());
+  }
+
+  int family = 0;
+  if (address->family() == Address::Family::INET4) {
+    family = AF_INET;
+  } else if (address->family() == Address::Family::INET6) {
+    family = AF_INET6;
+  } else {
+    return Failure("Unsupported address family. Windows only supports IP.");
+  }
+
+  Try<int_fd> accept_socket_ = net::socket(family, SOCK_STREAM, 0);
+  if (accept_socket_.isError()) {
+    return Failure(accept_socket_.error());
+  }
+
+  int_fd accept_socket = accept_socket_.get();
+
+  return windows::accept(self->get(), accept_socket)
+    .onAny([accept_socket](const Future<Nothing> future) {
+      if (!future.isReady()) {
+        os::close(accept_socket);
+      }
+    })
+    .then([self, accept_socket]() -> Future<std::shared_ptr<SocketImpl>> {
+      SOCKET listen = self->get();
+
+      // Inherit from the listening socket.
+      int res = ::setsockopt(
+          accept_socket,
+          SOL_SOCKET,
+          SO_UPDATE_ACCEPT_CONTEXT,
+          reinterpret_cast<char*>(&listen),
+          sizeof(listen));
+
+      if (res != 0) {
+        const WindowsError error;
+        os::close(accept_socket);
+        return Failure("Failed to set accepted socket: " + error.message);
+      }
+
+      // Disable Nagle algorithm, since we care about latency more than
+      // throughput. See https://en.wikipedia.org/wiki/Nagle%27s_algorithm
+      // for more info.
+      const int on = 1;
+      res = ::setsockopt(
+          accept_socket,
+          SOL_TCP,
+          TCP_NODELAY,
+          reinterpret_cast<const char*>(&on),
+          sizeof(on));
+
+      if (res != 0) {
+        const WindowsError error;
+        os::close(accept_socket);
+        return Failure(
+            "Failed to turn off the Nagle algorithm: " + error.message);
+      }
+
+      Try<Nothing> error = io::prepare_async(accept_socket);
+      if (error.isError()) {
+        os::close(accept_socket);
+        return Failure(
+            "Failed to set socket for asynchronous IO: " + error.error());
+      }
+
+      Try<std::shared_ptr<SocketImpl>> impl = create(accept_socket);
+      if (impl.isError()) {
+        os::close(accept_socket);
+        return Failure("Failed to create socket: " + impl.error());
+      }
+
+      return impl.get();
+    });
+}
+
+
+Future<Nothing> PollSocketImpl::connect(const Address& address)
+{
+  // Need to hold a copy of `this` so that the underlying socket
+  // doesn't end up getting reused before we return.
+  auto self = shared(this);
+
+  return windows::connect(self->get(), address)
+    .then([self]() -> Future<Nothing> {
+      // After the async connect (`ConnectEx`) is called, the socket is in a
+      // "default" state, which means it doesn't have the previously set
+      // properties or options set. We need to set `SO_UPDATE_CONNECT_CONTEXT`
+      // so that it regains its properties. For more information, see
+      // https://msdn.microsoft.com/en-us/library/windows/desktop/ms737606(v=vs.85).aspx // NOLINT(whitespace/line_length)
+      int res = ::setsockopt(
+          self->get(), SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, nullptr, 0);
+
+      if (res != 0) {
+        WindowsError error;
+        return Failure("Failed to set connected socket: " + error.message);
+      }
+
+      return Nothing();
+    });
+}
+
+
+Future<size_t> PollSocketImpl::recv(char* data, size_t size)
+{
+  // Need to hold a copy of `this` so that the underlying socket
+  // doesn't end up getting reused before we return from the call to
+  // `io::read` and end up reading data incorrectly.
+  auto self = shared(this);
+
+  return io::read(get(), data, size).then([self](size_t length) {
+    return length;
+  });
+}
+
+
+Future<size_t> PollSocketImpl::send(const char* data, size_t size)
+{
+  CHECK(size > 0); // TODO(benh): Just return 0 if `size` is 0?
+
+  // Need to hold a copy of `this` so that the underlying socket
+  // doesn't end up getting reused before we return.
+  auto self = shared(this);
+
+  // TODO(benh): Reuse `io::write`? Or is `net::send` and
+  // `MSG_NOSIGNAL` critical here?
+  return io::write(get(), data, size).then([self](size_t length) {
+    return length;
+  });
+}
+
+
+Future<size_t> PollSocketImpl::sendfile(int_fd fd, off_t offset, size_t size)
+{
+  CHECK(size > 0); // TODO(benh): Just return 0 if `size` is 0?
+
+  // Need to hold a copy of `this` so that the underlying socket
+  // doesn't end up getting reused before we return.
+  auto self = shared(this);
+
+  return windows::sendfile(self->get(), fd, offset, size)
+    .then([self](size_t length) { return length; });
+}
+
+} // namespace internal {
+} // namespace network {
+} // namespace process {


[06/16] mesos git commit: Added `io::prepare_async` and `io::is_async` functions.

Posted by an...@apache.org.
Added `io::prepare_async` and `io::is_async` functions.

The `os::nonblock` and `os::isNonBlock` fucntions are stubs that only
do things for Windows sockets. For the IOCP implementation, we need to
associate the handles with the IOCP handel, which is created by
libprocess. Thus, we need these new functions to prepare the handle
for the async IO functions by either setting the hande to non-blocking
on POSIX systems or by associating it with an IOCP handle on Windows.

Review: https://reviews.apache.org/r/67386/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/b6f3d715
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/b6f3d715
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/b6f3d715

Branch: refs/heads/master
Commit: b6f3d715380884d2bd690508dd3ce2df323fa46d
Parents: 2310609
Author: Akash Gupta <ak...@hotmail.com>
Authored: Wed Jun 27 14:30:12 2018 -0700
Committer: Andrew Schwartzmeyer <an...@schwartzmeyer.com>
Committed: Wed Jun 27 15:06:10 2018 -0700

----------------------------------------------------------------------
 3rdparty/libprocess/include/process/io.hpp      | 30 +++++++-
 3rdparty/libprocess/src/io.cpp                  | 72 +++++++++++---------
 3rdparty/libprocess/src/socket.cpp              |  7 +-
 .../libprocess/src/tests/subprocess_tests.cpp   |  4 +-
 4 files changed, 75 insertions(+), 38 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/b6f3d715/3rdparty/libprocess/include/process/io.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/include/process/io.hpp b/3rdparty/libprocess/include/process/io.hpp
index cc2caf4..5329bfe 100644
--- a/3rdparty/libprocess/include/process/io.hpp
+++ b/3rdparty/libprocess/include/process/io.hpp
@@ -46,6 +46,30 @@ const short WRITE = 0x02;
 const size_t BUFFERED_READ_SIZE = 16*4096;
 
 /**
+ * Prepares a file descriptor to be ready for asynchronous IO. On POSIX
+ * systems, this sets the file descriptor to non-blocking. On Windows, this
+ * will assign the file descriptor to an IO completion port.
+ *
+ * NOTE: Because the IO completion port is only known at the libprocess level,
+ * we need this function instead of simply using stout's `os::nonblock` and
+ * `os::isNonblock` functions like we could do for POSIX systems.
+ *
+ * @return On success, returns Nothing. On error, returns an Error.
+ */
+Try<Nothing> prepare_async(int_fd fd);
+
+
+/**
+ * Checks if `io::prepare_async` has been called on the file descriptor.
+ *
+ * @return Returns if the file descriptor is asynchronous. An asynchronous
+ *     file descriptor is defined to be non-blocking on POSIX systems and
+ *     overlapped and associated with an IO completion port on Windows.
+ *     An error will be returned if the file descriptor is invalid.
+ */
+Try<bool> is_async(int_fd fd);
+
+/**
  * Returns the events (a subset of the events specified) that can be
  * performed on the specified file descriptor without blocking.
  *
@@ -58,7 +82,8 @@ Future<short> poll(int_fd fd, short events);
 
 /**
  * Performs a single non-blocking read by polling on the specified
- * file descriptor until any data can be be read.
+ * file descriptor until any data can be be read. `io::prepare_async`
+ * needs to be called beforehand.
  *
  * The future will become ready when some data is read (may be less than
  * the specified size).
@@ -85,7 +110,8 @@ Future<std::string> read(int_fd fd);
 
 /**
  * Performs a single non-blocking write by polling on the specified
- * file descriptor until data can be be written.
+ * file descriptor until data can be be written. `io::prepare_async`
+ * needs to be called beforehand.
  *
  * The future will become ready when some data is written (may be less than
  * the specified size of the data).

http://git-wip-us.apache.org/repos/asf/mesos/blob/b6f3d715/3rdparty/libprocess/src/io.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/io.cpp b/3rdparty/libprocess/src/io.cpp
index 97f2b17..70715e2 100644
--- a/3rdparty/libprocess/src/io.cpp
+++ b/3rdparty/libprocess/src/io.cpp
@@ -133,19 +133,33 @@ Future<size_t> write(int_fd fd, const void* data, size_t size)
 } // namespace internal {
 
 
+Try<Nothing> prepare_async(int_fd fd)
+{
+  // TODO(akagup): Add windows iocp.
+  return os::nonblock(fd);
+}
+
+
+Try<bool> is_async(int_fd fd)
+{
+  // TODO(akagup): Add windows iocp.
+  return os::isNonblock(fd);
+}
+
+
 Future<size_t> read(int_fd fd, void* data, size_t size)
 {
   process::initialize();
 
   // Check the file descriptor.
-  Try<bool> nonblock = os::isNonblock(fd);
-  if (nonblock.isError()) {
+  Try<bool> async = is_async(fd);
+  if (async.isError()) {
     // The file descriptor is not valid (e.g., has been closed).
-    return Failure("Failed to check if file descriptor was non-blocking: " +
-                   nonblock.error());
-  } else if (!nonblock.get()) {
-    // The file descriptor is not non-blocking.
-    return Failure("Expected a non-blocking file descriptor");
+    return Failure(
+        "Failed to check if file descriptor was asynchronous: " +
+        async.error());
+  } else if (!async.get()) {
+    return Failure("Expected an asynchronous file descriptor.");
   }
 
   return internal::read(fd, data, size);
@@ -157,15 +171,14 @@ Future<size_t> write(int_fd fd, const void* data, size_t size)
   process::initialize();
 
   // Check the file descriptor.
-  Try<bool> nonblock = os::isNonblock(fd);
-  if (nonblock.isError()) {
+  Try<bool> async = is_async(fd);
+  if (async.isError()) {
     // The file descriptor is not valid (e.g., has been closed).
     return Failure(
-        "Failed to check if file descriptor was non-blocking: " +
-        nonblock.error());
-  } else if (!nonblock.get()) {
-    // The file descriptor is not non-blocking.
-    return Failure("Expected a non-blocking file descriptor");
+        "Failed to check if file descriptor was asynchronous: " +
+        async.error());
+  } else if (!async.get()) {
+    return Failure("Expected an asynchronous file descriptor.");
   }
 
   return internal::write(fd, data, size);
@@ -237,13 +250,12 @@ Future<string> read(int_fd fd)
         cloexec.error());
   }
 
-  // Make the file descriptor non-blocking.
-  Try<Nothing> nonblock = os::nonblock(fd);
-  if (nonblock.isError()) {
+  Try<Nothing> async = prepare_async(fd);
+  if (async.isError()) {
     os::close(fd);
     return Failure(
-        "Failed to make duplicated file descriptor non-blocking: " +
-        nonblock.error());
+        "Failed to make duplicated file descriptor asynchronous: " +
+        async.error());
   }
 
   // TODO(benh): Wrap up this data as a struct, use 'Owner'.
@@ -298,13 +310,12 @@ Future<Nothing> write(int_fd fd, const string& data)
         cloexec.error());
   }
 
-  // Make the file descriptor non-blocking.
-  Try<Nothing> nonblock = os::nonblock(fd);
-  if (nonblock.isError()) {
+  Try<Nothing> async = prepare_async(fd);
+  if (async.isError()) {
     os::close(fd);
     return Failure(
-        "Failed to make duplicated file descriptor non-blocking: " +
-        nonblock.error());
+        "Failed to make duplicated file descriptor asynchronous: " +
+        async.error());
   }
 
   // We store `data.size()` so that we can just use `size` in the
@@ -389,19 +400,18 @@ Future<Nothing> redirect(
     return Failure("Failed to set close-on-exec on 'to': " + cloexec.error());
   }
 
-  // Make the file descriptors non-blocking (no-op if already set).
-  Try<Nothing> nonblock = os::nonblock(from);
-  if (nonblock.isError()) {
+  Try<Nothing> async = prepare_async(from);
+  if (async.isError()) {
     os::close(from);
     os::close(to.get());
-    return Failure("Failed to make 'from' non-blocking: " + nonblock.error());
+    return Failure("Failed to make 'from' asynchronous: " + async.error());
   }
 
-  nonblock = os::nonblock(to.get());
-  if (nonblock.isError()) {
+  async = prepare_async(to.get());
+  if (async.isError()) {
     os::close(from);
     os::close(to.get());
-    return Failure("Failed to make 'to' non-blocking: " + nonblock.error());
+    return Failure("Failed to make 'to' asynchronous: " + async.error());
   }
 
   // NOTE: We wrap `os::close` in a lambda to disambiguate on Windows.

http://git-wip-us.apache.org/repos/asf/mesos/blob/b6f3d715/3rdparty/libprocess/src/socket.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/socket.cpp b/3rdparty/libprocess/src/socket.cpp
index 504cb54..3031cd8 100644
--- a/3rdparty/libprocess/src/socket.cpp
+++ b/3rdparty/libprocess/src/socket.cpp
@@ -15,6 +15,7 @@
 
 #include <boost/shared_array.hpp>
 
+#include <process/io.hpp>
 #include <process/loop.hpp>
 #include <process/network.hpp>
 #include <process/owned.hpp>
@@ -79,10 +80,10 @@ Try<std::shared_ptr<SocketImpl>> SocketImpl::create(
     return Error("Failed to create socket: " + s.error());
   }
 
-  Try<Nothing> nonblock = os::nonblock(s.get());
-  if (nonblock.isError()) {
+  Try<Nothing> async = io::prepare_async(s.get());
+  if (async.isError()) {
     os::close(s.get());
-    return Error("Failed to create socket, nonblock: " + nonblock.error());
+    return Error("Failed to create socket, prepare_async: " + async.error());
   }
 
   Try<Nothing> cloexec = os::cloexec(s.get());

http://git-wip-us.apache.org/repos/asf/mesos/blob/b6f3d715/3rdparty/libprocess/src/tests/subprocess_tests.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/tests/subprocess_tests.cpp b/3rdparty/libprocess/src/tests/subprocess_tests.cpp
index 568d77b..b12490e 100644
--- a/3rdparty/libprocess/src/tests/subprocess_tests.cpp
+++ b/3rdparty/libprocess/src/tests/subprocess_tests.cpp
@@ -465,10 +465,10 @@ TEST_F(SubprocessTest, PipeRedirect)
       S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
 
   ASSERT_SOME(fd);
-  ASSERT_SOME(os::nonblock(fd.get()));
+  ASSERT_SOME(io::prepare_async(fd.get()));
 
   ASSERT_SOME(s->out());
-  ASSERT_SOME(os::nonblock(s->out().get()));
+  ASSERT_SOME(io::prepare_async(s->out().get()));
   AWAIT_READY(io::redirect(s->out().get(), fd.get()));
 
   // Close our copy of the fd.


[04/16] mesos git commit: Windows: Made socket `int_fd` castable to `HANDLE` type.

Posted by an...@apache.org.
Windows: Made socket `int_fd` castable to `HANDLE` type.

Many Win32 functions that take in `HANDLE` also work on `SOCKET`, such
as `ReadFile` or `CreateIoCompletionPort`. IOCP on Windows uses the
`HANDLE` type uniformly, so we need to be able to convert socket
`int_fd` to `HANDLE`.

Review: https://reviews.apache.org/r/67384/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/f7f1c457
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/f7f1c457
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/f7f1c457

Branch: refs/heads/master
Commit: f7f1c45739443109b9451472578746d21f7cef99
Parents: f6f35a8
Author: Akash Gupta <ak...@hotmail.com>
Authored: Wed Jun 27 14:30:11 2018 -0700
Committer: Andrew Schwartzmeyer <an...@schwartzmeyer.com>
Committed: Wed Jun 27 15:06:10 2018 -0700

----------------------------------------------------------------------
 3rdparty/stout/include/stout/os/windows/fd.hpp | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/f7f1c457/3rdparty/stout/include/stout/os/windows/fd.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/stout/include/stout/os/windows/fd.hpp b/3rdparty/stout/include/stout/os/windows/fd.hpp
index 89a037a..b472658 100644
--- a/3rdparty/stout/include/stout/os/windows/fd.hpp
+++ b/3rdparty/stout/include/stout/os/windows/fd.hpp
@@ -153,7 +153,15 @@ public:
 
   operator HANDLE() const
   {
-    CHECK_EQ(Type::HANDLE, type());
+    // A `SOCKET` can be treated as a regular file `HANDLE` [1]. There are
+    // many Win32 functions that work on a `SOCKET` but have the `HANDLE`
+    // type as a function parameter like `CreateIoCompletionPort`, so we need
+    // to be able to cast a `SOCKET` based `int_fd` to a `HANDLE`.
+    //
+    // [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms740522(v=vs.85).aspx // NOLINT(whitespace/line_length)
+    if (type() == Type::SOCKET) {
+      return reinterpret_cast<HANDLE>(socket_);
+    }
     return handle_;
   }
 


[09/16] mesos git commit: Organized POSIX and Windows libprocess implementations.

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/poll_socket.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/poll_socket.cpp b/3rdparty/libprocess/src/poll_socket.cpp
deleted file mode 100644
index 74acb69..0000000
--- a/3rdparty/libprocess/src/poll_socket.cpp
+++ /dev/null
@@ -1,278 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License
-
-
-#ifdef __WINDOWS__
-#include <stout/windows.hpp>
-#else
-#include <netinet/tcp.h>
-#endif // __WINDOWS__
-
-#include <process/io.hpp>
-#include <process/loop.hpp>
-#include <process/network.hpp>
-#include <process/socket.hpp>
-
-#include <stout/os/sendfile.hpp>
-#include <stout/os/strerror.hpp>
-#include <stout/os.hpp>
-
-#include "config.hpp"
-#include "poll_socket.hpp"
-
-using std::string;
-
-namespace process {
-namespace network {
-namespace internal {
-
-Try<std::shared_ptr<SocketImpl>> PollSocketImpl::create(int_fd s)
-{
-  return std::make_shared<PollSocketImpl>(s);
-}
-
-
-Try<Nothing> PollSocketImpl::listen(int backlog)
-{
-  if (::listen(get(), backlog) < 0) {
-    return ErrnoError();
-  }
-  return Nothing();
-}
-
-
-Future<std::shared_ptr<SocketImpl>> PollSocketImpl::accept()
-{
-  // Need to hold a copy of `this` so that the underlying socket
-  // doesn't end up getting reused before we return from the call to
-  // `io::poll` and end up accepting a socket incorrectly.
-  auto self = shared(this);
-
-  return io::poll(get(), io::READ)
-    .then([self]() -> Future<std::shared_ptr<SocketImpl>> {
-      Try<int_fd> accepted = network::accept(self->get());
-      if (accepted.isError()) {
-        return Failure(accepted.error());
-      }
-
-      int_fd s = accepted.get();
-      Try<Nothing> nonblock = os::nonblock(s);
-      if (nonblock.isError()) {
-        os::close(s);
-        return Failure("Failed to accept, nonblock: " + nonblock.error());
-      }
-
-      Try<Nothing> cloexec = os::cloexec(s);
-      if (cloexec.isError()) {
-        os::close(s);
-        return Failure("Failed to accept, cloexec: " + cloexec.error());
-      }
-
-      Try<Address> address = network::address(s);
-      if (address.isError()) {
-        os::close(s);
-        return Failure("Failed to get address: " + address.error());
-      }
-
-      // Turn off Nagle (TCP_NODELAY) so pipelined requests don't wait.
-      // NOTE: We cast to `char*` here because the function prototypes
-      // on Windows use `char*` instead of `void*`.
-      if (address->family() == Address::Family::INET4 ||
-          address->family() == Address::Family::INET6) {
-        int on = 1;
-        if (::setsockopt(
-                s,
-                SOL_TCP,
-                TCP_NODELAY,
-                reinterpret_cast<const char*>(&on),
-                sizeof(on)) < 0) {
-          const string error = os::strerror(errno);
-          os::close(s);
-          return Failure(
-              "Failed to turn off the Nagle algorithm: " + stringify(error));
-        }
-      }
-
-      Try<std::shared_ptr<SocketImpl>> impl = create(s);
-      if (impl.isError()) {
-        os::close(s);
-        return Failure("Failed to create socket: " + impl.error());
-      }
-
-      return impl.get();
-    });
-}
-
-
-Future<Nothing> PollSocketImpl::connect(const Address& address)
-{
-  Try<Nothing, SocketError> connect = network::connect(get(), address);
-  if (connect.isError()) {
-    if (net::is_inprogress_error(connect.error().code)) {
-      // Need to hold a copy of `this` so that the underlying socket
-      // doesn't end up getting reused before we return from the call
-      // to `io::poll` and end up connecting incorrectly.
-      auto self = shared(this);
-
-      return io::poll(get(), io::WRITE)
-        .then([self, address]() -> Future<Nothing> {
-          // Now check that a successful connection was made.
-          int opt;
-          socklen_t optlen = sizeof(opt);
-
-          // NOTE: We cast to `char*` here because the function
-          // prototypes on Windows use `char*` instead of `void*`.
-          if (::getsockopt(
-                  self->get(),
-                  SOL_SOCKET,
-                  SO_ERROR,
-                  reinterpret_cast<char*>(&opt),
-                  &optlen) < 0) {
-            return Failure(SocketError(
-                "Failed to get status of connect to " + stringify(address)));
-          }
-
-          if (opt != 0) {
-            return Failure(SocketError(
-                opt,
-                "Failed to connect to " +
-                stringify(address)));
-          }
-
-          return Nothing();
-        });
-    }
-
-    return Failure(connect.error());
-  }
-
-  return Nothing();
-}
-
-
-Future<size_t> PollSocketImpl::recv(char* data, size_t size)
-{
-  // Need to hold a copy of `this` so that the underlying socket
-  // doesn't end up getting reused before we return from the call to
-  // `io::read` and end up reading data incorrectly.
-  auto self = shared(this);
-
-  return io::read(get(), data, size)
-    .then([self](size_t length) {
-      return length;
-    });
-}
-
-
-Future<size_t> PollSocketImpl::send(const char* data, size_t size)
-{
-  CHECK(size > 0); // TODO(benh): Just return 0 if `size` is 0?
-
-  // Need to hold a copy of `this` so that the underlying socket
-  // doesn't end up getting reused before we return.
-  auto self = shared(this);
-
-  // TODO(benh): Reuse `io::write`? Or is `net::send` and
-  // `MSG_NOSIGNAL` critical here?
-  return loop(
-      None(),
-      [self, data, size]() -> Future<Option<size_t>> {
-        while (true) {
-          ssize_t length = net::send(self->get(), data, size, MSG_NOSIGNAL);
-
-          if (length < 0) {
-#ifdef __WINDOWS__
-            int error = WSAGetLastError();
-#else
-            int error = errno;
-#endif // __WINDOWS__
-
-            if (net::is_restartable_error(error)) {
-              // Interrupted, try again now.
-              continue;
-            } else if (!net::is_retryable_error(error)) {
-              // TODO(benh): Confirm that `os::strerror` does the
-              // right thing for `error` on Windows.
-              VLOG(1) << "Socket error while sending: " << os::strerror(error);
-              return Failure(os::strerror(error));
-            }
-
-            return None();
-          }
-
-          return length;
-        }
-      },
-      [self](const Option<size_t>& length) -> Future<ControlFlow<size_t>> {
-        // Retry after we've polled if we don't yet have a result.
-        if (length.isNone()) {
-          return io::poll(self->get(), io::WRITE)
-            .then([](short event) -> ControlFlow<size_t> {
-              CHECK_EQ(io::WRITE, event);
-              return Continue();
-            });
-        }
-        return Break(length.get());
-      });
-}
-
-
-Future<size_t> PollSocketImpl::sendfile(int_fd fd, off_t offset, size_t size)
-{
-  CHECK(size > 0); // TODO(benh): Just return 0 if `size` is 0?
-
-  // Need to hold a copy of `this` so that the underlying socket
-  // doesn't end up getting reused before we return.
-  auto self = shared(this);
-
-  return loop(
-      None(),
-      [self, fd, offset, size]() -> Future<Option<size_t>> {
-        while (true) {
-          Try<ssize_t, SocketError> length = os::sendfile(
-              self->get(),
-              fd,
-              offset,
-              size);
-
-          if (length.isSome()) {
-            CHECK(length.get() >= 0);
-            return length.get();
-          }
-
-          if (net::is_restartable_error(length.error().code)) {
-            // Interrupted, try again now.
-            continue;
-          } else if (!net::is_retryable_error(length.error().code)) {
-            VLOG(1) << length.error().message;
-            return Failure(length.error());
-          }
-
-          return None();
-        }
-      },
-      [self](const Option<size_t>& length) -> Future<ControlFlow<size_t>> {
-        // Retry after we've polled if we don't yet have a result.
-        if (length.isNone()) {
-          return io::poll(self->get(), io::WRITE)
-            .then([](short event) -> ControlFlow<size_t> {
-              CHECK_EQ(io::WRITE, event);
-              return Continue();
-            });
-        }
-        return Break(length.get());
-      });
-}
-
-} // namespace internal {
-} // namespace network {
-} // namespace process {

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/posix/io.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/posix/io.cpp b/3rdparty/libprocess/src/posix/io.cpp
new file mode 100644
index 0000000..3862e3b
--- /dev/null
+++ b/3rdparty/libprocess/src/posix/io.cpp
@@ -0,0 +1,140 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#include <process/future.hpp>
+#include <process/io.hpp>
+#include <process/loop.hpp>
+
+#include <stout/error.hpp>
+#include <stout/none.hpp>
+#include <stout/option.hpp>
+#include <stout/os.hpp>
+
+#include <stout/os/int_fd.hpp>
+#include <stout/os/read.hpp>
+#include <stout/os/socket.hpp>
+#include <stout/os/write.hpp>
+
+#include "io_internal.hpp"
+
+namespace process {
+namespace io {
+namespace internal {
+
+Future<size_t> read(int_fd fd, void* data, size_t size)
+{
+  // TODO(benh): Let the system calls do what ever they're supposed to
+  // rather than return 0 here?
+  if (size == 0) {
+    return 0;
+  }
+
+  return loop(
+      None(),
+      [=]() -> Future<Option<size_t>> {
+        // Because the file descriptor is non-blocking, we call
+        // read()/recv() immediately. If no data is available than
+        // we'll call `poll` and block. 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).
+        ssize_t length = os::read(fd, data, size);
+        if (length < 0) {
+#ifdef __WINDOWS__
+          WindowsSocketError error;
+#else
+          ErrnoError error;
+#endif // __WINDOWS__
+
+          if (!net::is_restartable_error(error.code) &&
+              !net::is_retryable_error(error.code)) {
+            return Failure(error.message);
+          }
+
+          return None();
+        }
+
+        return length;
+      },
+      [=](const Option<size_t>& length) -> Future<ControlFlow<size_t>> {
+        // Restart/retry if we don't yet have a result.
+        if (length.isNone()) {
+          return io::poll(fd, io::READ)
+            .then([](short event) -> ControlFlow<size_t> {
+              CHECK_EQ(io::READ, event);
+              return Continue();
+            });
+        }
+        return Break(length.get());
+      });
+}
+
+
+Future<size_t> write(int_fd fd, const void* data, size_t size)
+{
+  // TODO(benh): Let the system calls do what ever they're supposed to
+  // rather than return 0 here?
+  if (size == 0) {
+    return 0;
+  }
+
+  return loop(
+      None(),
+      [=]() -> Future<Option<size_t>> {
+        ssize_t length = os::write(fd, data, size);
+
+        if (length < 0) {
+#ifdef __WINDOWS__
+          WindowsSocketError error;
+#else
+          ErrnoError error;
+#endif // __WINDOWS__
+
+          if (!net::is_restartable_error(error.code) &&
+              !net::is_retryable_error(error.code)) {
+            return Failure(error.message);
+          }
+
+          return None();
+        }
+
+        return length;
+      },
+      [=](const Option<size_t>& length) -> Future<ControlFlow<size_t>> {
+        // Restart/retry if we don't yet have a result.
+        if (length.isNone()) {
+          return io::poll(fd, io::WRITE)
+            .then([](short event) -> ControlFlow<size_t> {
+              CHECK_EQ(io::WRITE, event);
+              return Continue();
+            });
+        }
+        return Break(length.get());
+      });
+}
+
+
+Try<Nothing> prepare_async(int_fd fd)
+{
+  return os::nonblock(fd);
+}
+
+
+Try<bool> is_async(int_fd fd)
+{
+  return os::isNonblock(fd);
+}
+
+} // namespace internal {
+} // namespace io {
+} // namespace process {

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/posix/libev/libev.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/posix/libev/libev.cpp b/3rdparty/libprocess/src/posix/libev/libev.cpp
new file mode 100644
index 0000000..173ee46
--- /dev/null
+++ b/3rdparty/libprocess/src/posix/libev/libev.cpp
@@ -0,0 +1,166 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#include <ev.h>
+
+#include <mutex>
+#include <queue>
+
+#include <stout/duration.hpp>
+#include <stout/lambda.hpp>
+#include <stout/nothing.hpp>
+
+#include "event_loop.hpp"
+#include "libev.hpp"
+
+namespace process {
+
+ev_async async_watcher;
+// We need an asynchronous watcher to receive the request to shutdown.
+ev_async shutdown_watcher;
+
+// Define the initial values for all of the declarations made in
+// libev.hpp (since these need to live in the static data space).
+struct ev_loop* loop = nullptr;
+
+std::queue<ev_io*>* watchers = new std::queue<ev_io*>();
+
+std::mutex* watchers_mutex = new std::mutex();
+
+std::queue<lambda::function<void()>>* functions =
+  new std::queue<lambda::function<void()>>();
+
+thread_local bool* _in_event_loop_ = nullptr;
+
+
+void handle_async(struct ev_loop* loop, ev_async* _, int revents)
+{
+  std::queue<lambda::function<void()>> run_functions;
+  synchronized (watchers_mutex) {
+    // Start all the new I/O watchers.
+    while (!watchers->empty()) {
+      ev_io* watcher = watchers->front();
+      watchers->pop();
+      ev_io_start(loop, watcher);
+    }
+
+    // Swap the functions into a temporary queue so that we can invoke
+    // them outside of the mutex.
+    std::swap(run_functions, *functions);
+  }
+
+  // Running the functions outside of the mutex reduces locking
+  // contention as these are arbitrary functions that can take a long
+  // time to execute. Doing this also avoids a deadlock scenario where
+  // (A) mutexes are acquired before calling `run_in_event_loop`,
+  // followed by locking (B) `watchers_mutex`. If we executed the
+  // functions inside the mutex, then the locking order violation
+  // would be this function acquiring the (B) `watchers_mutex`
+  // followed by the arbitrary function acquiring the (A) mutexes.
+  while (!run_functions.empty()) {
+    (run_functions.front())();
+    run_functions.pop();
+  }
+}
+
+
+void handle_shutdown(struct ev_loop* loop, ev_async* _, int revents)
+{
+  ev_unloop(loop, EVUNLOOP_ALL);
+}
+
+
+void EventLoop::initialize()
+{
+  loop = ev_default_loop(EVFLAG_AUTO);
+
+  ev_async_init(&async_watcher, handle_async);
+  ev_async_init(&shutdown_watcher, handle_shutdown);
+
+  ev_async_start(loop, &async_watcher);
+  ev_async_start(loop, &shutdown_watcher);
+}
+
+
+namespace internal {
+
+void handle_delay(struct ev_loop* loop, ev_timer* timer, int revents)
+{
+  lambda::function<void()>* function =
+    reinterpret_cast<lambda::function<void()>*>(timer->data);
+  (*function)();
+  delete function;
+  ev_timer_stop(loop, timer);
+  delete timer;
+}
+
+
+Future<Nothing> delay(
+    const Duration& duration,
+    const lambda::function<void()>& function)
+{
+  ev_timer* timer = new ev_timer();
+  timer->data = reinterpret_cast<void*>(new lambda::function<void()>(function));
+
+  // Determine the 'after' parameter to pass to libev and set it to 0
+  // in the event that it's negative so that we always make sure to
+  // invoke 'function' even if libev doesn't support negative 'after'
+  // values.
+  double after = duration.secs();
+
+  if (after < 0) {
+    after = 0;
+  }
+
+  const double repeat = 0.0;
+
+  ev_timer_init(timer, handle_delay, after, repeat);
+  ev_timer_start(loop, timer);
+
+  return Nothing();
+}
+
+} // namespace internal {
+
+
+void EventLoop::delay(
+    const Duration& duration,
+    const lambda::function<void()>& function)
+{
+  run_in_event_loop<Nothing>(
+      lambda::bind(&internal::delay, duration, function));
+}
+
+
+double EventLoop::time()
+{
+  // TODO(benh): Versus ev_now()?
+  return ev_time();
+}
+
+
+void EventLoop::run()
+{
+  __in_event_loop__ = true;
+
+  ev_loop(loop, 0);
+
+  __in_event_loop__ = false;
+}
+
+
+void EventLoop::stop()
+{
+  ev_async_send(loop, &shutdown_watcher);
+}
+
+} // namespace process {

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/posix/libev/libev.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/posix/libev/libev.hpp b/3rdparty/libprocess/src/posix/libev/libev.hpp
new file mode 100644
index 0000000..d451931
--- /dev/null
+++ b/3rdparty/libprocess/src/posix/libev/libev.hpp
@@ -0,0 +1,96 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#ifndef __LIBEV_HPP__
+#define __LIBEV_HPP__
+
+#include <ev.h>
+
+#include <mutex>
+#include <queue>
+
+#include <process/future.hpp>
+#include <process/owned.hpp>
+
+#include <stout/lambda.hpp>
+#include <stout/synchronized.hpp>
+
+namespace process {
+
+// Event loop.
+extern struct ev_loop* loop;
+
+// Asynchronous watcher for interrupting loop to specifically deal
+// with IO watchers and functions (via run_in_event_loop).
+extern ev_async async_watcher;
+
+// Queue of I/O watchers to be asynchronously added to the event loop
+// (protected by 'watchers' below).
+// TODO(benh): Replace this queue with functions that we put in
+// 'functions' below that perform the ev_io_start themselves.
+extern std::queue<ev_io*>* watchers;
+extern std::mutex* watchers_mutex;
+
+// Queue of functions to be invoked asynchronously within the vent
+// loop (protected by 'watchers' above).
+extern std::queue<lambda::function<void()>>* functions;
+
+// Per thread bool pointer. We use a pointer to lazily construct the
+// actual bool.
+extern thread_local bool* _in_event_loop_;
+
+#define __in_event_loop__ *(_in_event_loop_ == nullptr ?                \
+  _in_event_loop_ = new bool(false) : _in_event_loop_)
+
+
+// Wrapper around function we want to run in the event loop.
+template <typename T>
+void _run_in_event_loop(
+    const lambda::function<Future<T>()>& f,
+    const Owned<Promise<T>>& promise)
+{
+  // Don't bother running the function if the future has been discarded.
+  if (promise->future().hasDiscard()) {
+    promise->discard();
+  } else {
+    promise->set(f());
+  }
+}
+
+
+// Helper for running a function in the event loop.
+template <typename T>
+Future<T> run_in_event_loop(const lambda::function<Future<T>()>& f)
+{
+  // If this is already the event loop then just run the function.
+  if (__in_event_loop__) {
+    return f();
+  }
+
+  Owned<Promise<T>> promise(new Promise<T>());
+
+  Future<T> future = promise->future();
+
+  // Enqueue the function.
+  synchronized (watchers_mutex) {
+    functions->push(lambda::bind(&_run_in_event_loop<T>, f, promise));
+  }
+
+  // Interrupt the loop.
+  ev_async_send(loop, &async_watcher);
+
+  return future;
+}
+
+} // namespace process {
+
+#endif // __LIBEV_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/posix/libev/libev_poll.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/posix/libev/libev_poll.cpp b/3rdparty/libprocess/src/posix/libev/libev_poll.cpp
new file mode 100644
index 0000000..96913a6
--- /dev/null
+++ b/3rdparty/libprocess/src/posix/libev/libev_poll.cpp
@@ -0,0 +1,142 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#include <ev.h>
+
+#include <memory>
+
+#include <process/future.hpp>
+#include <process/process.hpp> // For process::initialize.
+
+#include <stout/lambda.hpp>
+
+#include "libev.hpp"
+
+namespace process {
+
+// Data necessary for polling so we can discard polling and actually
+// stop it in the event loop.
+struct Poll
+{
+  Poll()
+  {
+    // Need to explicitly instantiate the watchers.
+    watcher.io.reset(new ev_io());
+    watcher.async.reset(new ev_async());
+  }
+
+  // An I/O watcher for checking for readability or writeability and
+  // an async watcher for being able to discard the polling.
+  struct {
+    std::shared_ptr<ev_io> io;
+    std::shared_ptr<ev_async> async;
+  } watcher;
+
+  Promise<short> promise;
+};
+
+
+// Event loop callback when I/O is ready on polling file descriptor.
+void polled(struct ev_loop* loop, ev_io* watcher, int revents)
+{
+  Poll* poll = (Poll*) watcher->data;
+
+  ev_io_stop(loop, poll->watcher.io.get());
+
+  // Stop the async watcher (also clears if pending so 'discard_poll'
+  // will not get invoked and we can delete 'poll' here).
+  ev_async_stop(loop, poll->watcher.async.get());
+
+  poll->promise.set(revents);
+
+  delete poll;
+}
+
+
+// Event loop callback when future associated with polling file
+// descriptor has been discarded.
+void discard_poll(struct ev_loop* loop, ev_async* watcher, int revents)
+{
+  Poll* poll = (Poll*) watcher->data;
+
+  // Check and see if we have a pending 'polled' callback and if so
+  // let it "win".
+  if (ev_is_pending(poll->watcher.io.get())) {
+    return;
+  }
+
+  ev_async_stop(loop, poll->watcher.async.get());
+
+  // Stop the I/O watcher (but note we check if pending above) so it
+  // won't get invoked and we can delete 'poll' here.
+  ev_io_stop(loop, poll->watcher.io.get());
+
+  poll->promise.discard();
+
+  delete poll;
+}
+
+
+namespace io {
+namespace internal {
+
+// Helper/continuation of 'poll' on future discard.
+void _poll(const std::shared_ptr<ev_async>& async)
+{
+  ev_async_send(loop, async.get());
+}
+
+
+Future<short> poll(int_fd fd, short events)
+{
+  Poll* poll = new Poll();
+
+  // Have the watchers data point back to the struct.
+  poll->watcher.async->data = poll;
+  poll->watcher.io->data = poll;
+
+  // Get a copy of the future to avoid any races with the event loop.
+  Future<short> future = poll->promise.future();
+
+  // Initialize and start the async watcher.
+  ev_async_init(poll->watcher.async.get(), discard_poll);
+  ev_async_start(loop, poll->watcher.async.get());
+
+  // Make sure we stop polling if a discard occurs on our future.
+  // Note that it's possible that we'll invoke '_poll' when someone
+  // does a discard even after the polling has already completed, but
+  // in this case while we will interrupt the event loop since the
+  // async watcher has already been stopped we won't cause
+  // 'discard_poll' to get invoked.
+  future.onDiscard(lambda::bind(&_poll, poll->watcher.async));
+
+  // Initialize and start the I/O watcher.
+  ev_io_init(poll->watcher.io.get(), polled, fd, events);
+  ev_io_start(loop, poll->watcher.io.get());
+
+  return future;
+}
+
+} // namespace internal {
+
+
+Future<short> poll(int_fd fd, short events)
+{
+  process::initialize();
+
+  // TODO(benh): Check if the file descriptor is non-blocking?
+
+  return run_in_event_loop<short>(lambda::bind(&internal::poll, fd, events));
+}
+
+} // namespace io {
+} // namespace process {

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/posix/libevent/libevent.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/posix/libevent/libevent.cpp b/3rdparty/libprocess/src/posix/libevent/libevent.cpp
new file mode 100644
index 0000000..fb595bc
--- /dev/null
+++ b/3rdparty/libprocess/src/posix/libevent/libevent.cpp
@@ -0,0 +1,211 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#ifndef __WINDOWS__
+#include <unistd.h>
+#endif // __WINDOWS__
+
+#include <mutex>
+
+#include <event2/event.h>
+#include <event2/thread.h>
+#include <event2/util.h>
+
+#include <process/logging.hpp>
+#include <process/once.hpp>
+
+#include <stout/synchronized.hpp>
+
+#include "event_loop.hpp"
+#include "libevent.hpp"
+
+namespace process {
+
+event_base* base = nullptr;
+
+
+static std::mutex* functions_mutex = new std::mutex();
+std::queue<lambda::function<void()>>* functions =
+  new std::queue<lambda::function<void()>>();
+
+
+thread_local bool* _in_event_loop_ = nullptr;
+
+
+void async_function(evutil_socket_t socket, short which, void* arg)
+{
+  event* ev = reinterpret_cast<event*>(arg);
+  event_free(ev);
+
+  std::queue<lambda::function<void()>> q;
+
+  synchronized (functions_mutex) {
+    std::swap(q, *functions);
+  }
+
+  while (!q.empty()) {
+    q.front()();
+    q.pop();
+  }
+}
+
+
+void run_in_event_loop(
+    const lambda::function<void()>& f,
+    EventLoopLogicFlow event_loop_logic_flow)
+{
+  if (__in_event_loop__ && event_loop_logic_flow == ALLOW_SHORT_CIRCUIT) {
+    f();
+    return;
+  }
+
+  synchronized (functions_mutex) {
+    functions->push(f);
+
+    // Add an event and activate it to interrupt the event loop.
+    // TODO(jmlvanre): after libevent v 2.1 we can use
+    // event_self_cbarg instead of re-assigning the event. For now we
+    // manually re-assign the event to pass in the pointer to the
+    // event itself as the callback argument.
+    event* ev = evtimer_new(base, async_function, nullptr);
+
+    // 'event_assign' is only valid on non-pending AND non-active
+    // events. This means we have to assign the callback before
+    // calling 'event_active'.
+    if (evtimer_assign(ev, base, async_function, ev) < 0) {
+      LOG(FATAL) << "Failed to assign callback on event";
+    }
+
+    event_active(ev, EV_TIMEOUT, 0);
+  }
+}
+
+
+void EventLoop::run()
+{
+  __in_event_loop__ = true;
+
+  do {
+    int result = event_base_loop(base, EVLOOP_ONCE);
+    if (result < 0) {
+      LOG(FATAL) << "Failed to run event loop";
+    } else if (result > 0) {
+      // All events are handled, continue event loop.
+      continue;
+    } else {
+      CHECK_EQ(0, result);
+      if (event_base_got_break(base)) {
+        break;
+      } else if (event_base_got_exit(base)) {
+        break;
+      }
+    }
+  } while (true);
+
+  __in_event_loop__ = false;
+}
+
+
+void EventLoop::stop()
+{
+  event_base_loopexit(base, nullptr);
+}
+
+
+namespace internal {
+
+struct Delay
+{
+  lambda::function<void()> function;
+  event* timer;
+};
+
+void handle_delay(evutil_socket_t, short, void* arg)
+{
+  Delay* delay = reinterpret_cast<Delay*>(arg);
+  delay->function();
+  event_free(delay->timer);
+  delete delay;
+}
+
+}  // namespace internal {
+
+
+void EventLoop::delay(
+    const Duration& duration,
+    const lambda::function<void()>& function)
+{
+  internal::Delay* delay = new internal::Delay();
+  delay->timer = evtimer_new(base, &internal::handle_delay, delay);
+  if (delay->timer == nullptr) {
+    LOG(FATAL) << "Failed to delay, evtimer_new";
+  }
+
+  delay->function = function;
+
+  timeval t{0, 0};
+  if (duration > Seconds(0)) {
+    t = duration.timeval();
+  }
+
+  evtimer_add(delay->timer, &t);
+}
+
+
+double EventLoop::time()
+{
+  // We explicitly call `evutil_gettimeofday()` for now to avoid any
+  // issues that may be introduced by using the cached value provided
+  // by `event_base_gettimeofday_cached()`. Since a lot of logic in
+  // libprocess depends on time math, we want to log fatal rather than
+  // cause logic errors if the time fails.
+  timeval t;
+  if (evutil_gettimeofday(&t, nullptr) < 0) {
+    LOG(FATAL) << "Failed to get time, evutil_gettimeofday";
+  }
+
+  return Duration(t).secs();
+}
+
+
+void EventLoop::initialize()
+{
+  static Once* initialized = new Once();
+
+  if (initialized->once()) {
+    return;
+  }
+
+  // We need to initialize Libevent differently depending on the
+  // operating system threading support.
+#if defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED)
+  if (evthread_use_pthreads() < 0) {
+    LOG(FATAL) << "Failed to initialize, evthread_use_pthreads";
+  }
+#elif defined(EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED)
+  if (evthread_use_windows_threads() < 0) {
+    LOG(FATAL) << "Failed to initialize, evthread_use_windows_threads";
+  }
+#else
+#error "Libevent must be compiled with either pthread or Windows thread support"
+#endif
+
+  base = event_base_new();
+
+  if (base == nullptr) {
+    LOG(FATAL) << "Failed to initialize, event_base_new";
+  }
+
+  initialized->done();
+}
+
+} // namespace process {

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/posix/libevent/libevent.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/posix/libevent/libevent.hpp b/3rdparty/libprocess/src/posix/libevent/libevent.hpp
new file mode 100644
index 0000000..2eb9790
--- /dev/null
+++ b/3rdparty/libprocess/src/posix/libevent/libevent.hpp
@@ -0,0 +1,48 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#ifndef __LIBEVENT_HPP__
+#define __LIBEVENT_HPP__
+
+#include <event2/event.h>
+
+#include <stout/lambda.hpp>
+
+namespace process {
+
+// Event loop.
+extern event_base* base;
+
+
+// Per thread bool pointer. We use a pointer to lazily construct the
+// actual bool.
+extern thread_local bool* _in_event_loop_;
+
+
+#define __in_event_loop__ *(_in_event_loop_ == nullptr ?                \
+  _in_event_loop_ = new bool(false) : _in_event_loop_)
+
+
+enum EventLoopLogicFlow
+{
+  ALLOW_SHORT_CIRCUIT,
+  DISALLOW_SHORT_CIRCUIT
+};
+
+
+void run_in_event_loop(
+    const lambda::function<void()>& f,
+    EventLoopLogicFlow event_loop_logic_flow = ALLOW_SHORT_CIRCUIT);
+
+} // namespace process {
+
+#endif // __LIBEVENT_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/posix/libevent/libevent_poll.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/posix/libevent/libevent_poll.cpp b/3rdparty/libprocess/src/posix/libevent/libevent_poll.cpp
new file mode 100644
index 0000000..038dde2
--- /dev/null
+++ b/3rdparty/libprocess/src/posix/libevent/libevent_poll.cpp
@@ -0,0 +1,112 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#include <event2/event.h>
+
+#include <memory>
+
+#include <process/future.hpp>
+#include <process/io.hpp>
+#include <process/process.hpp> // For process::initialize.
+
+#include "libevent.hpp"
+
+namespace process {
+
+namespace io {
+namespace internal {
+
+struct Poll
+{
+  Promise<short> promise;
+  std::shared_ptr<event> ev;
+};
+
+
+void pollCallback(evutil_socket_t, short what, void* arg)
+{
+  Poll* poll = reinterpret_cast<Poll*>(arg);
+
+  if (poll->promise.future().hasDiscard()) {
+    poll->promise.discard();
+  } else {
+    // Convert libevent specific EV_READ / EV_WRITE to io::* specific
+    // values of these enumerations.
+    short events =
+      ((what & EV_READ) ? io::READ : 0) | ((what & EV_WRITE) ? io::WRITE : 0);
+
+    poll->promise.set(events);
+  }
+
+  // Deleting the `poll` also destructs `ev` and hence triggers `event_free`,
+  // which makes the event non-pending.
+  delete poll;
+}
+
+
+void pollDiscard(const std::weak_ptr<event>& ev, short events)
+{
+  // Discarding inside the event loop prevents `pollCallback()` from being
+  // called twice if the future is discarded.
+  run_in_event_loop([=]() {
+    std::shared_ptr<event> shared = ev.lock();
+    // If `ev` cannot be locked `pollCallback` already ran. If it was locked
+    // but not pending, `pollCallback` is scheduled to be executed.
+    if (static_cast<bool>(shared) &&
+        event_pending(shared.get(), events, nullptr)) {
+      // `event_active` will trigger the `pollCallback` to be executed.
+      event_active(shared.get(), EV_READ, 0);
+    }
+  });
+}
+
+} // namespace internal {
+
+
+Future<short> poll(int_fd fd, short events)
+{
+  process::initialize();
+
+  internal::Poll* poll = new internal::Poll();
+
+  Future<short> future = poll->promise.future();
+
+  // Convert io::READ / io::WRITE to libevent specific values of these
+  // enumerations.
+  short what =
+    ((events & io::READ) ? EV_READ : 0) | ((events & io::WRITE) ? EV_WRITE : 0);
+
+  // Bind `event_free` to the destructor of the `ev` shared pointer
+  // guaranteeing that the event will be freed only once.
+  poll->ev.reset(
+      event_new(base, fd, what, &internal::pollCallback, poll),
+      event_free);
+
+  if (poll->ev == nullptr) {
+    LOG(FATAL) << "Failed to poll, event_new";
+  }
+
+  // Using a `weak_ptr` prevents `ev` to become a dangling pointer if
+  // the returned future is discarded after the event is triggered.
+  // The `weak_ptr` needs to be created before `event_add` in case
+  // the event is ready and the callback is executed before creating
+  // `ev`.
+  std::weak_ptr<event> ev(poll->ev);
+
+  event_add(poll->ev.get(), nullptr);
+
+  return future
+    .onDiscard(lambda::bind(&internal::pollDiscard, ev, what));
+}
+
+} // namespace io {
+} // namespace process {

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/posix/libevent/libevent_ssl_socket.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/posix/libevent/libevent_ssl_socket.cpp b/3rdparty/libprocess/src/posix/libevent/libevent_ssl_socket.cpp
new file mode 100644
index 0000000..436b389
--- /dev/null
+++ b/3rdparty/libprocess/src/posix/libevent/libevent_ssl_socket.cpp
@@ -0,0 +1,1249 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#include <event2/buffer.h>
+#include <event2/bufferevent_ssl.h>
+#include <event2/event.h>
+#include <event2/listener.h>
+#include <event2/thread.h>
+#include <event2/util.h>
+
+#include <openssl/ssl.h>
+#include <openssl/err.h>
+
+#include <process/queue.hpp>
+#include <process/socket.hpp>
+
+#include <process/ssl/flags.hpp>
+
+#include <stout/net.hpp>
+#include <stout/synchronized.hpp>
+
+#include <stout/os/close.hpp>
+#include <stout/os/dup.hpp>
+#include <stout/os/fcntl.hpp>
+
+#include "libevent.hpp"
+#include "libevent_ssl_socket.hpp"
+#include "openssl.hpp"
+#include "poll_socket.hpp"
+
+// Locking:
+//
+// We use the BEV_OPT_THREADSAFE flag when constructing bufferevents
+// so that all **functions that are called from the event loop that
+// take a bufferevent as a parameter will automatically have the
+// lock acquired**.
+//
+// This means that everywhere that the libevent library does not
+// already lock the bev, we need to manually 'synchronize (bev) {'.
+// To further complicate matters, due to a deadlock scneario in
+// libevent-openssl (v 2.0.21) we currently modify bufferevents using
+// continuations in the event loop, but these functions, while run
+// from within the event loop, are not passed the 'bev' as a parameter
+// and thus MUST use 'synchronized (bev)'. See 'Continuation' comment
+// below for more details on why we need to invoke these continuations
+// from within the event loop.
+
+// Continuations via 'run_in_event_loop(...)':
+//
+// There is a deadlock scenario in libevent-openssl (v 2.0.21) when
+// modifying the bufferevent (bev) from another thread (not the event
+// loop). To avoid this we run all bufferevent manipulation logic in
+// continuations that are executed within the event loop.
+
+// DISALLOW_SHORT_CIRCUIT:
+//
+// We disallow short-circuiting in 'run_in_event_loop' due to a bug in
+// libevent_openssl with deferred callbacks still being called (still
+// in the run queue) even though a bev has been disabled.
+
+using std::queue;
+using std::string;
+
+// Specialization of 'synchronize' to use bufferevent with the
+// 'synchronized' macro.
+static Synchronized<bufferevent> synchronize(bufferevent* bev)
+{
+  return Synchronized<bufferevent>(
+      bev,
+      [](bufferevent* bev) { bufferevent_lock(bev); },
+      [](bufferevent* bev) { bufferevent_unlock(bev); });
+}
+
+namespace process {
+namespace network {
+namespace internal {
+
+Try<std::shared_ptr<SocketImpl>> LibeventSSLSocketImpl::create(int_fd s)
+{
+  openssl::initialize();
+
+  if (!openssl::flags().enabled) {
+    return Error("SSL is disabled");
+  }
+
+  auto socket = std::make_shared<LibeventSSLSocketImpl>(s);
+  // See comment at 'initialize' declaration for why we call this.
+  socket->initialize();
+  return socket;
+}
+
+
+LibeventSSLSocketImpl::~LibeventSSLSocketImpl()
+{
+  // We defer termination and destruction of all event loop specific
+  // calls and structures. This is a safety against the socket being
+  // destroyed before existing event loop calls have completed since
+  // they require valid data structures (the weak pointer).
+  //
+  // Release ownership of the file descriptor so that
+  // we can defer closing the socket.
+  int_fd fd = release();
+  CHECK(fd >= 0);
+
+  evconnlistener* _listener = listener;
+  bufferevent* _bev = bev;
+  std::weak_ptr<LibeventSSLSocketImpl>* _event_loop_handle = event_loop_handle;
+
+  run_in_event_loop(
+      [_listener, _bev, _event_loop_handle, fd]() {
+        // Once this lambda is called, it should not be possible for
+        // more event loop callbacks to be triggered with 'this->bev'.
+        // This is important because we delete event_loop_handle which
+        // is the callback argument for any event loop callbacks.
+        // This lambda is responsible for ensuring 'this->bev' is
+        // disabled, and cleaning up any remaining state associated
+        // with the event loop.
+
+        CHECK(__in_event_loop__);
+
+        if (_listener != nullptr) {
+          evconnlistener_free(_listener);
+        }
+
+        if (_bev != nullptr) {
+          // NOTE: Removes all future callbacks using 'this->bev'.
+          bufferevent_disable(_bev, EV_READ | EV_WRITE);
+
+          SSL* ssl = bufferevent_openssl_get_ssl(_bev);
+          SSL_free(ssl);
+          bufferevent_free(_bev);
+        }
+
+        CHECK_SOME(os::close(fd)) << "Failed to close socket";
+
+        delete _event_loop_handle;
+      },
+      DISALLOW_SHORT_CIRCUIT);
+}
+
+
+void LibeventSSLSocketImpl::initialize()
+{
+  event_loop_handle = new std::weak_ptr<LibeventSSLSocketImpl>(shared(this));
+}
+
+
+Try<Nothing, SocketError> LibeventSSLSocketImpl::shutdown(int how)
+{
+  // Nothing to do if this socket was never initialized.
+  synchronized (lock) {
+    if (bev == nullptr) {
+      // If it was not initialized, then there should also be no
+      // requests.
+      CHECK(connect_request.get() == nullptr);
+      CHECK(recv_request.get() == nullptr);
+      CHECK(send_request.get() == nullptr);
+
+      // We expect this to fail and generate an 'ENOTCONN' failure as
+      // no connection should exist at this point.
+      if (::shutdown(s, how) < 0) {
+        return SocketError();
+      }
+
+      return Nothing();
+    }
+  }
+
+  // Extend the life-time of 'this' through the execution of the
+  // lambda in the event loop. Note: The 'self' needs to be explicitly
+  // captured because we're not using it in the body of the lambda. We
+  // can use a 'shared_ptr' because run_in_event_loop is guaranteed to
+  // execute.
+  auto self = shared(this);
+
+  run_in_event_loop(
+      [self]() {
+        CHECK(__in_event_loop__);
+        CHECK(self);
+
+        CHECK_NOTNULL(self->bev);
+
+        synchronized (self->bev) {
+          Owned<RecvRequest> request;
+
+          // Swap the 'recv_request' under the object lock.
+          synchronized (self->lock) {
+            std::swap(request, self->recv_request);
+          }
+
+          // If there is still a pending receive request then close it.
+          if (request.get() != nullptr) {
+            request->promise
+              .set(bufferevent_read(self->bev, request->data, request->size));
+          }
+
+          // Workaround for SSL shutdown, see http://www.wangafu.net/~nickm/libevent-book/Ref6a_advanced_bufferevents.html // NOLINT
+          SSL* ssl = bufferevent_openssl_get_ssl(self->bev);
+          SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
+          SSL_shutdown(ssl);
+        }
+      },
+      DISALLOW_SHORT_CIRCUIT);
+
+  return Nothing();
+}
+
+
+// Only runs in event loop. No locks required. See 'Locking' note at
+// top of file.
+void LibeventSSLSocketImpl::recv_callback(bufferevent* /*bev*/, void* arg)
+{
+  CHECK(__in_event_loop__);
+
+  std::weak_ptr<LibeventSSLSocketImpl>* handle =
+    reinterpret_cast<std::weak_ptr<LibeventSSLSocketImpl>*>(CHECK_NOTNULL(arg));
+
+  std::shared_ptr<LibeventSSLSocketImpl> impl(handle->lock());
+
+  // Don't call the 'recv_callback' unless the socket is still valid.
+  if (impl != nullptr) {
+    impl->recv_callback();
+  }
+}
+
+
+// Only runs in event loop. Member function continuation of static
+// 'recv_callback'. This function can be called from two places -
+// a) `LibeventSSLSocketImpl::recv` when a new Socket::recv is called and there
+//    is buffer available to read.
+// b) `LibeventSSLSocketImpl::recv_callback when libevent calls the deferred
+//    read callback.
+void LibeventSSLSocketImpl::recv_callback()
+{
+  CHECK(__in_event_loop__);
+
+  Owned<RecvRequest> request;
+
+  const size_t buffer_length = evbuffer_get_length(bufferevent_get_input(bev));
+
+  // Swap out the request object IFF there is buffer available to read. We check
+  // this here because it is possible that when the libevent deferred callback
+  // was called, a Socket::recv context already read the buffer from the event.
+  // Following sequence is possible:
+  // a. libevent finds a buffer ready to be read.
+  // b. libevent queues buffer event to be dispatched.
+  // c. Socket::recv is called that creates a new request.
+  // d. Socket::recv finds buffer length > 0.
+  // e. Socket::recv reads the buffer.
+  // f. A new request Socket::recv is called which creates a new request.
+  // g. libevent callback is called for the event queued at step b.
+  // h. libevent callback finds the length of the buffer as 0 but the request is
+  //    a non-nullptr due to step f.
+  if (buffer_length > 0 || received_eof) {
+    synchronized (lock) {
+      std::swap(request, recv_request);
+    }
+  }
+
+  if (request.get() != nullptr) {
+    if (buffer_length > 0) {
+      size_t length = bufferevent_read(bev, request->data, request->size);
+      CHECK(length > 0);
+
+      request->promise.set(length);
+    } else {
+      CHECK(received_eof);
+      request->promise.set(0);
+    }
+  }
+}
+
+
+// Only runs in event loop. No locks required. See 'Locking' note at
+// top of file.
+void LibeventSSLSocketImpl::send_callback(bufferevent* /*bev*/, void* arg)
+{
+  CHECK(__in_event_loop__);
+
+  std::weak_ptr<LibeventSSLSocketImpl>* handle =
+    reinterpret_cast<std::weak_ptr<LibeventSSLSocketImpl>*>(CHECK_NOTNULL(arg));
+
+  std::shared_ptr<LibeventSSLSocketImpl> impl(handle->lock());
+
+  // Don't call the 'send_callback' unless the socket is still valid.
+  if (impl != nullptr) {
+    impl->send_callback();
+  }
+}
+
+
+// Only runs in event loop. Member function continuation of static
+// 'recv_callback'.
+void LibeventSSLSocketImpl::send_callback()
+{
+  CHECK(__in_event_loop__);
+
+  Owned<SendRequest> request;
+
+  synchronized (lock) {
+    std::swap(request, send_request);
+  }
+
+  if (request.get() != nullptr) {
+    request->promise.set(request->size);
+  }
+}
+
+
+// Only runs in event loop. No locks required. See 'Locking' note at
+// top of file.
+void LibeventSSLSocketImpl::event_callback(
+    bufferevent* /*bev*/,
+    short events,
+    void* arg)
+{
+  CHECK(__in_event_loop__);
+
+  std::weak_ptr<LibeventSSLSocketImpl>* handle =
+    reinterpret_cast<std::weak_ptr<LibeventSSLSocketImpl>*>(CHECK_NOTNULL(arg));
+
+  std::shared_ptr<LibeventSSLSocketImpl> impl(handle->lock());
+
+  // Don't call the 'event_callback' unless the socket is still valid.
+  if (impl != nullptr) {
+    impl->event_callback(events);
+  }
+}
+
+
+// Only runs in event loop. Member function continuation of static
+// 'recv_callback'.
+void LibeventSSLSocketImpl::event_callback(short events)
+{
+  CHECK(__in_event_loop__);
+
+  // TODO(bmahler): Libevent's invariant is that `events` contains:
+  //
+  //   (1) one of BEV_EVENT_READING or BEV_EVENT_WRITING to
+  //       indicate whether the event was on the read or write path.
+  //
+  //   (2) one of BEV_EVENT_EOF, BEV_EVENT_ERROR, BEV_EVENT_TIMEOUT,
+  //       BEV_EVENT_CONNECTED.
+  //
+  // (1) allows us to handle read and write errors separately.
+  // HOWEVER, for SSL bufferevents in 2.0.x, libevent never seems
+  // to tell us about BEV_EVENT_READING or BEV_EVENT_WRITING,
+  // which forces us to write incorrect logic here by treating all
+  // events as affecting both reads and writes.
+  //
+  // This has been fixed in 2.1.x:
+  //   2.1 "What's New":
+  //     https://github.com/libevent/libevent/blob/release-2.1.8-stable/whatsnew-2.1.txt#L333-L335 // NOLINT
+  //   Commit:
+  //     https://github.com/libevent/libevent/commit/f7eb69ace
+  //
+  // We should require 2.1.x so that we can correctly distinguish
+  // between the read and write errors, and not have two code paths
+  // depending on the libevent version, see MESOS-5999, MESOS-6770.
+
+  Owned<RecvRequest> current_recv_request;
+  Owned<SendRequest> current_send_request;
+  Owned<ConnectRequest> current_connect_request;
+
+  if (events & BEV_EVENT_EOF ||
+      events & BEV_EVENT_CONNECTED ||
+      events & BEV_EVENT_ERROR) {
+    synchronized (lock) {
+      std::swap(current_recv_request, recv_request);
+      std::swap(current_send_request, send_request);
+      std::swap(current_connect_request, connect_request);
+    }
+  }
+
+  // First handle EOF, we also look for `BEV_EVENT_ERROR` with
+  // `EVUTIL_SOCKET_ERROR() == 0` since this occurs as a result
+  // of a "dirty" SSL shutdown (i.e. TCP close before SSL close)
+  // or when this socket has been shut down and further sends
+  // are performed.
+  //
+  // TODO(bmahler): We don't expose "dirty" SSL shutdowns as
+  // recv errors, but perhaps we should?
+  if (events & BEV_EVENT_EOF ||
+     (events & BEV_EVENT_ERROR && EVUTIL_SOCKET_ERROR() == 0)) {
+    received_eof = true;
+
+    if (current_recv_request.get() != nullptr) {
+      // Drain any remaining data from the bufferevent or complete the
+      // promise with 0 to signify EOF. Because we set `received_eof`,
+      // subsequent calls to `recv` will return 0 if there is no data
+      // remaining on the buffer.
+      if (evbuffer_get_length(bufferevent_get_input(bev)) > 0) {
+        size_t length =
+          bufferevent_read(
+              bev,
+              current_recv_request->data,
+              current_recv_request->size);
+        CHECK(length > 0);
+
+        current_recv_request->promise.set(length);
+      } else {
+        current_recv_request->promise.set(0);
+      }
+    }
+
+    if (current_send_request.get() != nullptr) {
+      current_send_request->promise.fail("Failed send: connection closed");
+    }
+
+    if (current_connect_request.get() != nullptr) {
+      SSL* ssl = bufferevent_openssl_get_ssl(CHECK_NOTNULL(bev));
+      SSL_free(ssl);
+      bufferevent_free(CHECK_NOTNULL(bev));
+      bev = nullptr;
+      current_connect_request->promise.fail(
+          "Failed connect: connection closed");
+    }
+  } else if (events & BEV_EVENT_CONNECTED) {
+    // We should not have receiving or sending request while still
+    // connecting.
+    CHECK(current_recv_request.get() == nullptr);
+    CHECK(current_send_request.get() == nullptr);
+    CHECK_NOTNULL(current_connect_request.get());
+
+    // If we're connecting, then we've succeeded. Time to do
+    // post-verification.
+    CHECK_NOTNULL(bev);
+
+    // Do post-validation of connection.
+    SSL* ssl = bufferevent_openssl_get_ssl(bev);
+
+    Try<Nothing> verify = openssl::verify(ssl, peer_hostname, peer_ip);
+    if (verify.isError()) {
+      VLOG(1) << "Failed connect, verification error: " << verify.error();
+      SSL_free(ssl);
+      bufferevent_free(bev);
+      bev = nullptr;
+      current_connect_request->promise.fail(verify.error());
+      return;
+    }
+
+    current_connect_request->promise.set(Nothing());
+  } else if (events & BEV_EVENT_ERROR) {
+    CHECK(EVUTIL_SOCKET_ERROR() != 0);
+    std::ostringstream error_stream;
+    error_stream << evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR());
+
+    // If there is a valid error, fail any requests and log the error.
+    VLOG(1) << "Socket error: " << error_stream.str();
+
+    if (current_recv_request.get() != nullptr) {
+      current_recv_request->promise.fail(
+          "Failed recv, connection error: " +
+          error_stream.str());
+    }
+
+    if (current_send_request.get() != nullptr) {
+      current_send_request->promise.fail(
+          "Failed send, connection error: " +
+          error_stream.str());
+    }
+
+    if (current_connect_request.get() != nullptr) {
+      SSL* ssl = bufferevent_openssl_get_ssl(CHECK_NOTNULL(bev));
+      SSL_free(ssl);
+      bufferevent_free(CHECK_NOTNULL(bev));
+      bev = nullptr;
+      current_connect_request->promise.fail(
+          "Failed connect, connection error: " +
+          error_stream.str());
+    }
+  }
+}
+
+
+LibeventSSLSocketImpl::LibeventSSLSocketImpl(int_fd _s)
+  : SocketImpl(_s),
+    bev(nullptr),
+    listener(nullptr),
+    recv_request(nullptr),
+    send_request(nullptr),
+    connect_request(nullptr),
+    event_loop_handle(nullptr) {}
+
+
+LibeventSSLSocketImpl::LibeventSSLSocketImpl(
+    int_fd _s,
+    bufferevent* _bev,
+    Option<string>&& _peer_hostname)
+  : SocketImpl(_s),
+    bev(_bev),
+    listener(nullptr),
+    recv_request(nullptr),
+    send_request(nullptr),
+    connect_request(nullptr),
+    event_loop_handle(nullptr),
+    peer_hostname(std::move(_peer_hostname)) {}
+
+
+Future<Nothing> LibeventSSLSocketImpl::connect(const Address& address)
+{
+  if (bev != nullptr) {
+    return Failure("Socket is already connected");
+  }
+
+  if (connect_request.get() != nullptr) {
+    return Failure("Socket is already connecting");
+  }
+
+  SSL* ssl = SSL_new(openssl::context());
+  if (ssl == nullptr) {
+    return Failure("Failed to connect: SSL_new");
+  }
+
+  // Construct the bufferevent in the connecting state.
+  // We set 'BEV_OPT_DEFER_CALLBACKS' to avoid calling the
+  // 'event_callback' before 'bufferevent_socket_connect' returns.
+  CHECK(bev == nullptr);
+  bev = bufferevent_openssl_socket_new(
+      base,
+      s,
+      ssl,
+      BUFFEREVENT_SSL_CONNECTING,
+      BEV_OPT_THREADSAFE | BEV_OPT_DEFER_CALLBACKS);
+
+  if (bev == nullptr) {
+    // We need to free 'ssl' here because the bev won't clean it up
+    // for us.
+    SSL_free(ssl);
+    return Failure("Failed to connect: bufferevent_openssl_socket_new");
+  }
+
+  if (address.family() == Address::Family::INET4 ||
+      address.family() == Address::Family::INET6) {
+    // Try and determine the 'peer_hostname' from the address we're
+    // connecting to in order to properly verify the certificate
+    // later.
+    const Try<string> hostname =
+      network::convert<inet::Address>(address)->hostname();
+
+    if (hostname.isError()) {
+      VLOG(2) << "Could not determine hostname of peer: " << hostname.error();
+    } else {
+      VLOG(2) << "Connecting to " << hostname.get();
+      peer_hostname = hostname.get();
+    }
+
+    // Determine the 'peer_ip' from the address we're connecting to in
+    // order to properly verify the certificate later.
+    peer_ip = network::convert<inet::Address>(address)->ip;
+  }
+
+  // Optimistically construct a 'ConnectRequest' and future.
+  Owned<ConnectRequest> request(new ConnectRequest());
+  Future<Nothing> future = request->promise.future();
+
+  // Assign 'connect_request' under lock, fail on error.
+  synchronized (lock) {
+    if (connect_request.get() != nullptr) {
+      SSL_free(ssl);
+      bufferevent_free(bev);
+      bev = nullptr;
+      return Failure("Socket is already connecting");
+    }
+    std::swap(request, connect_request);
+  }
+
+  // Extend the life-time of 'this' through the execution of the
+  // lambda in the event loop. Note: The 'self' needs to be explicitly
+  // captured because we're not using it in the body of the lambda. We
+  // can use a 'shared_ptr' because run_in_event_loop is guaranteed to
+  // execute.
+  auto self = shared(this);
+
+  run_in_event_loop(
+      [self, address]() {
+        sockaddr_storage addr = address;
+
+          // Assign the callbacks for the bufferevent. We do this
+          // before the 'bufferevent_socket_connect()' call to avoid
+          // any race on the underlying buffer events becoming ready.
+          bufferevent_setcb(
+              self->bev,
+              &LibeventSSLSocketImpl::recv_callback,
+              &LibeventSSLSocketImpl::send_callback,
+              &LibeventSSLSocketImpl::event_callback,
+              CHECK_NOTNULL(self->event_loop_handle));
+
+          if (bufferevent_socket_connect(
+                  self->bev,
+                  reinterpret_cast<sockaddr*>(&addr),
+                  address.size()) < 0) {
+            SSL* ssl = bufferevent_openssl_get_ssl(CHECK_NOTNULL(self->bev));
+            SSL_free(ssl);
+            bufferevent_free(self->bev);
+            self->bev = nullptr;
+
+            Owned<ConnectRequest> request;
+
+            // Swap out the 'connect_request' so we can destroy it
+            // outside of the lock.
+            synchronized (self->lock) {
+              std::swap(request, self->connect_request);
+            }
+
+            CHECK_NOTNULL(request.get());
+
+            // Fail the promise since we failed to connect.
+            request->promise.fail(
+                "Failed to connect: bufferevent_socket_connect");
+          }
+      },
+      DISALLOW_SHORT_CIRCUIT);
+
+  return future;
+}
+
+
+Future<size_t> LibeventSSLSocketImpl::recv(char* data, size_t size)
+{
+  // Optimistically construct a 'RecvRequest' and future.
+  Owned<RecvRequest> request(new RecvRequest(data, size));
+  std::weak_ptr<LibeventSSLSocketImpl> weak_self(shared(this));
+
+  // If the user of the future decides to 'discard', then we want to
+  // test whether the request was already satisfied.
+  // We capture a 'weak_ptr' to 'this' (as opposed to a 'shared_ptr')
+  // because the socket could be destroyed before this lambda is
+  // executed. If we used a 'shared_ptr' then this lambda could extend
+  // the life-time of 'this' unnecessarily.
+  Future<size_t> future = request->promise.future()
+    .onDiscard([weak_self]() {
+      // Extend the life-time of 'this' through the execution of the
+      // lambda in the event loop. Note: The 'self' needs to be
+      // explicitly captured because we're not using it in the body of
+      // the lambda. We can use a 'shared_ptr' because
+      // run_in_event_loop is guaranteed to execute.
+      std::shared_ptr<LibeventSSLSocketImpl> self(weak_self.lock());
+
+      if (self != nullptr) {
+        run_in_event_loop(
+            [self]() {
+              CHECK(__in_event_loop__);
+              CHECK(self);
+
+              Owned<RecvRequest> request;
+
+              synchronized (self->lock) {
+                std::swap(request, self->recv_request);
+              }
+
+              // Only discard if the request hasn't already been
+              // satisfied.
+              if (request.get() != nullptr) {
+                // Discard the promise outside of the object lock as
+                // the callbacks can be expensive.
+                request->promise.discard();
+              }
+            },
+            DISALLOW_SHORT_CIRCUIT);
+      }
+    });
+
+  // Assign 'recv_request' under lock, fail on error.
+  synchronized (lock) {
+    if (recv_request.get() != nullptr) {
+      return Failure("Socket is already receiving");
+    }
+    std::swap(request, recv_request);
+  }
+
+  // Extend the life-time of 'this' through the execution of the
+  // lambda in the event loop. Note: The 'self' needs to be explicitly
+  // captured because we're not using it in the body of the lambda. We
+  // can use a 'shared_ptr' because run_in_event_loop is guaranteed to
+  // execute.
+  auto self = shared(this);
+
+  run_in_event_loop(
+      [self]() {
+        CHECK(__in_event_loop__);
+        CHECK(self);
+
+        bool recv = false;
+
+        // We check to see if 'recv_request' is null. It would be null
+        // if a 'discard' happened before this lambda was executed.
+        synchronized (self->lock) {
+          recv = self->recv_request.get() != nullptr;
+        }
+
+        // Only try to read existing data from the bufferevent if the
+        // request has not already been discarded.
+        if (recv) {
+          synchronized (self->bev) {
+            evbuffer* input = bufferevent_get_input(self->bev);
+            size_t length = evbuffer_get_length(input);
+
+            // If there is already data in the buffer or an EOF has
+            // been received, fulfill the 'recv_request' by calling
+            // 'recv_callback()'. Otherwise do nothing and wait for
+            // the 'recv_callback' to run when we receive data over
+            // the network.
+            if (length > 0 || self->received_eof) {
+              self->recv_callback();
+            }
+          }
+        }
+      },
+      DISALLOW_SHORT_CIRCUIT);
+
+  return future;
+}
+
+
+Future<size_t> LibeventSSLSocketImpl::send(const char* data, size_t size)
+{
+  // Optimistically construct a 'SendRequest' and future.
+  Owned<SendRequest> request(new SendRequest(size));
+  Future<size_t> future = request->promise.future();
+
+  // We don't add an 'onDiscard' continuation to send because we can
+  // not accurately detect how many bytes have been sent. Once we pass
+  // the data to the bufferevent, there is the possibility that parts
+  // of it have been sent. Another reason is that if we send partial
+  // messages (discard only a part of the data), then it is likely
+  // that the receiving end will fail parsing the message.
+
+  // Assign 'send_request' under lock, fail on error.
+  synchronized (lock) {
+    if (send_request.get() != nullptr) {
+      return Failure("Socket is already sending");
+    }
+    std::swap(request, send_request);
+  }
+
+  evbuffer* buffer = CHECK_NOTNULL(evbuffer_new());
+
+  int result = evbuffer_add(buffer, data, size);
+  CHECK_EQ(0, result);
+
+  // Extend the life-time of 'this' through the execution of the
+  // lambda in the event loop. Note: The 'self' needs to be explicitly
+  // captured because we're not using it in the body of the lambda. We
+  // can use a 'shared_ptr' because run_in_event_loop is guaranteed to
+  // execute.
+  auto self = shared(this);
+
+  run_in_event_loop(
+      [self, buffer]() {
+        CHECK(__in_event_loop__);
+        CHECK(self);
+
+        // Check if the socket is closed or the write end has
+        // encountered an error in the interim (i.e. we received
+        // a BEV_EVENT_ERROR with BEV_EVENT_WRITING).
+        bool write = false;
+
+        synchronized (self->lock) {
+          if (self->send_request.get() != nullptr) {
+            write = true;
+          }
+        }
+
+        if (write) {
+          int result = bufferevent_write_buffer(self->bev, buffer);
+          CHECK_EQ(0, result);
+        }
+
+        evbuffer_free(buffer);
+      },
+      DISALLOW_SHORT_CIRCUIT);
+
+  return future;
+}
+
+
+Future<size_t> LibeventSSLSocketImpl::sendfile(
+    int_fd fd,
+    off_t offset,
+    size_t size)
+{
+  // Optimistically construct a 'SendRequest' and future.
+  Owned<SendRequest> request(new SendRequest(size));
+  Future<size_t> future = request->promise.future();
+
+  // Assign 'send_request' under lock, fail on error.
+  synchronized (lock) {
+    if (send_request.get() != nullptr) {
+      return Failure("Socket is already sending");
+    }
+    std::swap(request, send_request);
+  }
+
+  // Duplicate the file descriptor because Libevent will take ownership
+  // and control the lifecycle separately.
+  //
+  // TODO(josephw): We can avoid duplicating the file descriptor in
+  // future versions of Libevent. In Libevent versions 2.1.2 and later,
+  // we may use `evbuffer_file_segment_new` and `evbuffer_add_file_segment`
+  // instead of `evbuffer_add_file`.
+  Try<int_fd> dup = os::dup(fd);
+  if (dup.isError()) {
+    return Failure(dup.error());
+  }
+
+  // NOTE: This is *not* an `int_fd` because `libevent` requires a CRT
+  // integer file descriptor, which we allocate and then use
+  // exclusively here.
+#ifdef __WINDOWS__
+  int owned_fd = dup->crt();
+  // The `os::cloexec` and `os::nonblock` functions do nothing on
+  // Windows, and cannot be called because they take `int_fd`.
+#else
+  int owned_fd = dup.get();
+
+  // Set the close-on-exec flag.
+  Try<Nothing> cloexec = os::cloexec(owned_fd);
+  if (cloexec.isError()) {
+    os::close(owned_fd);
+    return Failure(
+        "Failed to set close-on-exec on duplicated file descriptor: " +
+        cloexec.error());
+  }
+
+  // Make the file descriptor non-blocking.
+  Try<Nothing> nonblock = os::nonblock(owned_fd);
+  if (nonblock.isError()) {
+    os::close(owned_fd);
+    return Failure(
+        "Failed to make duplicated file descriptor non-blocking: " +
+        nonblock.error());
+  }
+#endif // __WINDOWS__
+
+  // Extend the life-time of 'this' through the execution of the
+  // lambda in the event loop. Note: The 'self' needs to be explicitly
+  // captured because we're not using it in the body of the lambda. We
+  // can use a 'shared_ptr' because run_in_event_loop is guaranteed to
+  // execute.
+  auto self = shared(this);
+
+  run_in_event_loop(
+      [self, owned_fd, offset, size]() {
+        CHECK(__in_event_loop__);
+        CHECK(self);
+
+        // Check if the socket is closed or the write end has
+        // encountered an error in the interim (i.e. we received
+        // a BEV_EVENT_ERROR with BEV_EVENT_WRITING).
+        bool write = false;
+
+        synchronized (self->lock) {
+          if (self->send_request.get() != nullptr) {
+            write = true;
+          }
+        }
+
+        if (write) {
+          // NOTE: `evbuffer_add_file` will take ownership of the file
+          // descriptor and close it after it has finished reading it.
+          int result = evbuffer_add_file(
+              bufferevent_get_output(self->bev),
+              owned_fd,
+              offset,
+              size);
+          CHECK_EQ(0, result);
+        } else {
+#ifdef __WINDOWS__
+          // NOTE: `os::close()` on Windows is not compatible with CRT
+          // file descriptors, only `HANDLE` and `SOCKET` types.
+          ::_close(owned_fd);
+#else
+          os::close(owned_fd);
+#endif // __WINDOWS__
+        }
+      },
+      DISALLOW_SHORT_CIRCUIT);
+
+  return future;
+}
+
+
+Try<Nothing> LibeventSSLSocketImpl::listen(int backlog)
+{
+  if (listener != nullptr) {
+    return Error("Socket is already listening");
+  }
+
+  CHECK(bev == nullptr);
+
+  // NOTE: Accepted sockets are nonblocking by default in libevent, but
+  // can be set to block via the `LEV_OPT_LEAVE_SOCKETS_BLOCKING`
+  // flag for `evconnlistener_new`.
+  listener = evconnlistener_new(
+      base,
+      [](evconnlistener* listener,
+         evutil_socket_t socket,
+         sockaddr* addr,
+         int addr_length,
+         void* arg) {
+        CHECK(__in_event_loop__);
+
+        std::weak_ptr<LibeventSSLSocketImpl>* handle =
+          reinterpret_cast<std::weak_ptr<LibeventSSLSocketImpl>*>(
+              CHECK_NOTNULL(arg));
+
+        std::shared_ptr<LibeventSSLSocketImpl> impl(handle->lock());
+
+#ifndef __WINDOWS__
+        // NOTE: Passing the flag `LEV_OPT_CLOSE_ON_EXEC` into
+        // `evconnlistener_new` would atomically set `SOCK_CLOEXEC`
+        // on the accepted socket. However, this flag is not supported
+        // in the minimum recommended version of libevent (2.0.22).
+        Try<Nothing> cloexec = os::cloexec(socket);
+        if (cloexec.isError()) {
+          VLOG(2) << "Failed to accept, cloexec: " << cloexec.error();
+
+          // Propagate the error through the listener's `accept_queue`.
+          if (impl != nullptr) {
+            impl->accept_queue.put(
+                Failure("Failed to accept, cloexec: " + cloexec.error()));
+          }
+
+          os::close(socket);
+          return;
+        }
+#endif // __WINDOWS__
+
+        if (impl != nullptr) {
+          Try<net::IP> ip = net::IP::create(*addr);
+          if (ip.isError()) {
+            VLOG(2) << "Could not convert sockaddr to net::IP: " << ip.error();
+          }
+
+          // We pass the 'listener' into the 'AcceptRequest' because
+          // this function could be executed before 'this->listener'
+          // is set.
+          AcceptRequest* request =
+            new AcceptRequest(
+                  // NOTE: The `int_fd` must be explicitly constructed
+                  // to avoid the `intptr_t` being casted to an `int`,
+                  // resulting in a `HANDLE` instead of a `SOCKET` on
+                  // Windows.
+                  int_fd(socket),
+                  listener,
+                  ip.isSome() ? Option<net::IP>(ip.get()) : None());
+
+          impl->accept_callback(request);
+        }
+      },
+      event_loop_handle,
+      LEV_OPT_REUSEABLE,
+      backlog,
+      s);
+
+  if (listener == nullptr) {
+    return Error("Failed to listen on socket");
+  }
+
+  // TODO(jmlvanre): attach an error callback.
+
+  return Nothing();
+}
+
+
+Future<std::shared_ptr<SocketImpl>> LibeventSSLSocketImpl::accept()
+{
+  // Note that due to MESOS-8448, when the caller discards, it's
+  // possible that we pull an accepted socket out of the queue but
+  // drop it when `.then` transitions to discarded rather than
+  // executing the continuation. This is currently acceptable since
+  // callers only discard when they're breaking their accept loop.
+  // However, from an API perspective, we shouldn't be dropping
+  // the socket on the floor.
+  //
+  // We explicitly specify the return type to avoid a type deduction
+  // issue in some versions of clang. See MESOS-2943.
+  return accept_queue.get()
+    .then([](const Future<std::shared_ptr<SocketImpl>>& impl)
+      -> Future<std::shared_ptr<SocketImpl>> {
+      CHECK(!impl.isPending());
+      return impl;
+    });
+}
+
+
+void LibeventSSLSocketImpl::peek_callback(
+    evutil_socket_t fd,
+    short what,
+    void* arg)
+{
+  CHECK(__in_event_loop__);
+
+  CHECK(what & EV_READ);
+  char data[6];
+
+  // Try to peek the first 6 bytes of the message.
+  ssize_t size = ::recv(fd, data, 6, MSG_PEEK);
+
+  // Based on the function 'ssl23_get_client_hello' in openssl, we
+  // test whether to dispatch to the SSL or non-SSL based accept based
+  // on the following rules:
+  //   1. If there are fewer than 3 bytes: non-SSL.
+  //   2. If the 1st bit of the 1st byte is set AND the 3rd byte is
+  //          equal to SSL2_MT_CLIENT_HELLO: SSL.
+  //   3. If the 1st byte is equal to SSL3_RT_HANDSHAKE AND the 2nd
+  //      byte is equal to SSL3_VERSION_MAJOR and the 6th byte is
+  //      equal to SSL3_MT_CLIENT_HELLO: SSL.
+  //   4. Otherwise: non-SSL.
+
+  // For an ascii based protocol to falsely get dispatched to SSL it
+  // needs to:
+  //   1. Start with an invalid ascii character (0x80).
+  //   2. OR have the first 2 characters be a SYN followed by ETX, and
+  //          then the 6th character be SOH.
+  // These conditions clearly do not constitute valid HTTP requests,
+  // and are unlikely to collide with other existing protocols.
+
+  bool ssl = false; // Default to rule 4.
+
+  if (size < 2) { // Rule 1.
+    ssl = false;
+  } else if ((data[0] & 0x80) && data[2] == SSL2_MT_CLIENT_HELLO) { // Rule 2.
+    ssl = true;
+  } else if (data[0] == SSL3_RT_HANDSHAKE &&
+             data[1] == SSL3_VERSION_MAJOR &&
+             data[5] == SSL3_MT_CLIENT_HELLO) { // Rule 3.
+    ssl = true;
+  }
+
+  AcceptRequest* request = reinterpret_cast<AcceptRequest*>(arg);
+
+  // We call 'event_free()' here because it ensures the event is made
+  // non-pending and inactive before it gets deallocated.
+  event_free(request->peek_event);
+  request->peek_event = nullptr;
+
+  if (ssl) {
+    accept_SSL_callback(request);
+  } else {
+    // Downgrade to a non-SSL socket implementation.
+    //
+    // NOTE: The `int_fd` must be explicitly constructed to avoid the
+    // `intptr_t` being casted to an `int`, resulting in a `HANDLE`
+    // instead of a `SOCKET` on Windows.
+    Try<std::shared_ptr<SocketImpl>> impl = PollSocketImpl::create(int_fd(fd));
+    if (impl.isError()) {
+      request->promise.fail(impl.error());
+    } else {
+      request->promise.set(impl.get());
+    }
+
+    delete request;
+  }
+}
+
+
+void LibeventSSLSocketImpl::accept_callback(AcceptRequest* request)
+{
+  CHECK(__in_event_loop__);
+
+  Queue<Future<std::shared_ptr<SocketImpl>>> accept_queue_ = accept_queue;
+
+  // After the socket is accepted, it must complete the SSL
+  // handshake (or be downgraded to a regular socket) before
+  // we put it in the queue of connected sockets.
+  request->promise.future()
+    .onAny([accept_queue_](Future<std::shared_ptr<SocketImpl>> impl) mutable {
+      accept_queue_.put(impl);
+    });
+
+  // If we support downgrading the connection, first wait for this
+  // socket to become readable. We will then MSG_PEEK it to test
+  // whether we want to dispatch as SSL or non-SSL.
+  if (openssl::flags().support_downgrade) {
+    request->peek_event = event_new(
+        base,
+        request->socket,
+        EV_READ,
+        &LibeventSSLSocketImpl::peek_callback,
+        request);
+    event_add(request->peek_event, nullptr);
+  } else {
+    accept_SSL_callback(request);
+  }
+}
+
+
+void LibeventSSLSocketImpl::accept_SSL_callback(AcceptRequest* request)
+{
+  CHECK(__in_event_loop__);
+
+  // Set up SSL object.
+  SSL* ssl = SSL_new(openssl::context());
+  if (ssl == nullptr) {
+    request->promise.fail("Accept failed, SSL_new");
+    delete request;
+    return;
+  }
+
+  // We use 'request->listener' because 'this->listener' may not have
+  // been set by the time this function is executed. See comment in
+  // the lambda for evconnlistener_new in
+  // 'LibeventSSLSocketImpl::listen'.
+  event_base* ev_base = evconnlistener_get_base(request->listener);
+
+  // Construct the bufferevent in the accepting state.
+  bufferevent* bev = bufferevent_openssl_socket_new(
+      ev_base,
+      request->socket,
+      ssl,
+      BUFFEREVENT_SSL_ACCEPTING,
+      BEV_OPT_THREADSAFE);
+
+  if (bev == nullptr) {
+    request->promise.fail("Accept failed: bufferevent_openssl_socket_new");
+    SSL_free(ssl);
+    delete request;
+    return;
+  }
+
+  bufferevent_setcb(
+      bev,
+      nullptr,
+      nullptr,
+      [](bufferevent* bev, short events, void* arg) {
+        // This handles error states or 'BEV_EVENT_CONNECTED' events
+        // and satisfies the promise by constructing a new socket if
+        // the connection was successfuly established.
+        CHECK(__in_event_loop__);
+
+        AcceptRequest* request =
+          reinterpret_cast<AcceptRequest*>(CHECK_NOTNULL(arg));
+
+        if (events & BEV_EVENT_EOF) {
+          request->promise.fail("Failed accept: connection closed");
+        } else if (events & BEV_EVENT_CONNECTED) {
+          // We will receive a 'CONNECTED' state on an accepting socket
+          // once the connection is established. Time to do
+          // post-verification. First, we need to determine the peer
+          // hostname.
+          Option<string> peer_hostname = None();
+
+          if (request->ip.isSome()) {
+            Try<string> hostname = net::getHostname(request->ip.get());
+
+            if (hostname.isError()) {
+              VLOG(2) << "Could not determine hostname of peer: "
+                      << hostname.error();
+            } else {
+              VLOG(2) << "Accepting from " << hostname.get();
+              peer_hostname = hostname.get();
+            }
+          }
+
+          SSL* ssl = bufferevent_openssl_get_ssl(bev);
+          CHECK_NOTNULL(ssl);
+
+          Try<Nothing> verify =
+            openssl::verify(ssl, peer_hostname, request->ip);
+
+          if (verify.isError()) {
+            VLOG(1) << "Failed accept, verification error: " << verify.error();
+            request->promise.fail(verify.error());
+            SSL_free(ssl);
+            bufferevent_free(bev);
+            // TODO(jmlvanre): Clean up for readability. Consider RAII
+            // or constructing the impl earlier.
+            CHECK(request->socket >= 0);
+            Try<Nothing> close = os::close(request->socket);
+            if (close.isError()) {
+              LOG(FATAL)
+                << "Failed to close socket " << stringify(request->socket)
+                << ": " << close.error();
+            }
+            delete request;
+            return;
+          }
+
+          auto impl = std::shared_ptr<LibeventSSLSocketImpl>(
+              new LibeventSSLSocketImpl(
+                  request->socket,
+                  bev,
+                  std::move(peer_hostname)));
+
+          // See comment at 'initialize' declaration for why we call
+          // this.
+          impl->initialize();
+
+          // We have to wait till after 'initialize()' is invoked for
+          // event_loop_handle to be valid as a callback argument for
+          // the callbacks.
+          bufferevent_setcb(
+              CHECK_NOTNULL(impl->bev),
+              &LibeventSSLSocketImpl::recv_callback,
+              &LibeventSSLSocketImpl::send_callback,
+              &LibeventSSLSocketImpl::event_callback,
+              CHECK_NOTNULL(impl->event_loop_handle));
+
+          request->promise.set(std::dynamic_pointer_cast<SocketImpl>(impl));
+        } else if (events & BEV_EVENT_ERROR) {
+          std::ostringstream stream;
+          if (EVUTIL_SOCKET_ERROR() != 0) {
+            stream << evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR());
+          } else {
+            char buffer[1024] = {};
+            unsigned long error = bufferevent_get_openssl_error(bev);
+            ERR_error_string_n(error, buffer, sizeof(buffer));
+            stream << buffer;
+          }
+
+          // Fail the accept request and log the error.
+          VLOG(1) << "Socket error: " << stream.str();
+
+          SSL* ssl = bufferevent_openssl_get_ssl(CHECK_NOTNULL(bev));
+          SSL_free(ssl);
+          bufferevent_free(bev);
+
+          // TODO(jmlvanre): Clean up for readability. Consider RAII
+          // or constructing the impl earlier.
+          CHECK(request->socket >= 0);
+          Try<Nothing> close = os::close(request->socket);
+          if (close.isError()) {
+            LOG(FATAL)
+              << "Failed to close socket " << stringify(request->socket)
+              << ": " << close.error();
+          }
+          request->promise.fail(
+              "Failed accept: connection error: " + stream.str());
+        }
+
+        delete request;
+      },
+      request);
+}
+
+} // namespace internal {
+} // namespace network {
+} // namespace process {

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/posix/libevent/libevent_ssl_socket.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/posix/libevent/libevent_ssl_socket.hpp b/3rdparty/libprocess/src/posix/libevent/libevent_ssl_socket.hpp
new file mode 100644
index 0000000..6ef5a86
--- /dev/null
+++ b/3rdparty/libprocess/src/posix/libevent/libevent_ssl_socket.hpp
@@ -0,0 +1,198 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#ifndef __LIBEVENT_SSL_SOCKET_HPP__
+#define __LIBEVENT_SSL_SOCKET_HPP__
+
+#include <event2/buffer.h>
+#include <event2/bufferevent_ssl.h>
+#include <event2/event.h>
+#include <event2/listener.h>
+#include <event2/util.h>
+
+#include <atomic>
+#include <memory>
+
+#include <process/queue.hpp>
+#include <process/socket.hpp>
+
+namespace process {
+namespace network {
+namespace internal {
+
+class LibeventSSLSocketImpl : public SocketImpl
+{
+public:
+  // See 'Socket::create()'.
+  static Try<std::shared_ptr<SocketImpl>> create(int_fd s);
+
+  LibeventSSLSocketImpl(int_fd _s);
+
+  ~LibeventSSLSocketImpl() override;
+
+  // Implement 'SocketImpl' interface.
+  Future<Nothing> connect(const Address& address) override;
+  Future<size_t> recv(char* data, size_t size) override;
+  // Send does not currently support discard. See implementation.
+  Future<size_t> send(const char* data, size_t size) override;
+  Future<size_t> sendfile(int_fd fd, off_t offset, size_t size) override;
+  Try<Nothing> listen(int backlog) override;
+  Future<std::shared_ptr<SocketImpl>> accept() override;
+  SocketImpl::Kind kind() const override { return SocketImpl::Kind::SSL; }
+
+  // Shuts down the socket.
+  //
+  // NOTE: Although this method accepts an integer which specifies the
+  // shutdown mode, this parameter is ignored because SSL connections
+  // do not have a concept of read/write-only shutdown. If either end
+  // of the socket is closed, then the futures of any outstanding read
+  // requests will be completed (possibly as failures).
+  Try<Nothing, SocketError> shutdown(int how) override;
+
+  // We need a post-initializer because 'shared_from_this()' is not
+  // valid until the constructor has finished.
+  void initialize();
+
+private:
+  // A set of helper functions that transitions an accepted socket to
+  // an SSL connected socket. With the libevent-openssl library, once
+  // we return from the 'accept_callback()' which is scheduled by
+  // 'listen' then we still need to wait for the 'BEV_EVENT_CONNECTED'
+  // state before we know the SSL connection has been established.
+  struct AcceptRequest
+  {
+    AcceptRequest(
+        int_fd _socket,
+        evconnlistener* _listener,
+        const Option<net::IP>& _ip)
+      : peek_event(nullptr),
+        listener(_listener),
+        socket(_socket),
+        ip(_ip) {}
+    event* peek_event;
+    Promise<std::shared_ptr<SocketImpl>> promise;
+    evconnlistener* listener;
+    int_fd socket;
+    Option<net::IP> ip;
+  };
+
+  struct RecvRequest
+  {
+    RecvRequest(char* _data, size_t _size)
+      : data(_data), size(_size) {}
+    Promise<size_t> promise;
+    char* data;
+    size_t size;
+  };
+
+  struct SendRequest
+  {
+    SendRequest(size_t _size)
+      : size(_size) {}
+    Promise<size_t> promise;
+    size_t size;
+  };
+
+  struct ConnectRequest
+  {
+    Promise<Nothing> promise;
+  };
+
+  // This is a private constructor used by the accept helper
+  // functions.
+  LibeventSSLSocketImpl(
+      int_fd _s,
+      bufferevent* bev,
+      Option<std::string>&& peer_hostname);
+
+  // This is called when the equivalent of 'accept' returns. The role
+  // of this function is to set up the SSL object and bev. If we
+  // support both SSL and non-SSL traffic simultaneously then we first
+  // wait for data to be ready and test the hello handshake to
+  // disambiguate between the kinds of traffic.
+  void accept_callback(AcceptRequest* request);
+
+  // This is the continuation of 'accept_callback' that handles an SSL
+  // connection.
+  static void accept_SSL_callback(AcceptRequest* request);
+
+  // This function peeks at the data on an accepted socket to see if
+  // there is an SSL handshake or not. It then dispatches to the
+  // SSL handling function or creates a non-SSL socket.
+  static void peek_callback(evutil_socket_t fd, short what, void* arg);
+
+  // The following are function pairs of static functions to member
+  // functions. The static functions test and hold the weak pointer to
+  // the socket before calling the member functions. This protects
+  // against the socket being destroyed before the event loop calls
+  // the callbacks.
+  static void recv_callback(bufferevent* bev, void* arg);
+  void recv_callback();
+
+  static void send_callback(bufferevent* bev, void* arg);
+  void send_callback();
+
+  static void event_callback(bufferevent* bev, short events, void* arg);
+  void event_callback(short events);
+
+  bufferevent* bev;
+
+  evconnlistener* listener;
+
+  // Protects the following instance variables.
+  std::atomic_flag lock = ATOMIC_FLAG_INIT;
+  Owned<RecvRequest> recv_request;
+  Owned<SendRequest> send_request;
+  Owned<ConnectRequest> connect_request;
+
+  // Indicates whether or not an EOF has been received on this socket.
+  // Our accesses to this member are not synchronized because they all
+  // occur within the event loop, which runs on a single thread.
+  bool received_eof = false;
+
+  // This is a weak pointer to 'this', i.e., ourselves, this class
+  // instance. We need this for our event loop callbacks because it's
+  // possible that we'll actually want to cleanup this socket impl
+  // before the event loop callback gets executed ... and we'll check
+  // in each event loop callback whether or not this weak_ptr is valid
+  // by attempting to upgrade it to shared_ptr. It is the
+  // responsibility of the event loop through the deferred lambda in
+  // the destructor to clean up this pointer.
+  // 1) It is a 'weak_ptr' as opposed to a 'shared_ptr' because we
+  // want to test whether the object is still around from within the
+  // event loop. If it was a 'shared_ptr' then we would be
+  // contributing to the lifetime of the object and would no longer be
+  // able to test the lifetime correctly.
+  // 2) This is a pointer to a 'weak_ptr' so that we can pass this
+  // through to the event loop through the C-interface. We need access
+  // to the 'weak_ptr' from outside the object (in the event loop) to
+  // test if the object is still alive. By maintaining this 'weak_ptr'
+  // on the heap we can be sure it is safe to access from the
+  // event loop until it is destroyed.
+  std::weak_ptr<LibeventSSLSocketImpl>* event_loop_handle;
+
+  // This queue stores accepted sockets that are considered connected
+  // (either the SSL handshake has completed or the socket has been
+  // downgraded). The 'accept()' call returns sockets from this queue.
+  // We wrap the socket in a 'Future' so that we can pass failures or
+  // discards through.
+  Queue<Future<std::shared_ptr<SocketImpl>>> accept_queue;
+
+  Option<std::string> peer_hostname;
+  Option<net::IP> peer_ip;
+};
+
+} // namespace internal {
+} // namespace network {
+} // namespace process {
+
+#endif // __LIBEVENT_SSL_SOCKET_HPP__


[14/16] mesos git commit: Added Windows IOCP backend to libprocess's CMake build.

Posted by an...@apache.org.
Added Windows IOCP backend to libprocess's CMake build.

With the CMake option `ENABLE_LIBWINIO`, we can now conditionally turn
on the Windows IOCP backend for libprocess.


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/e2cba0eb
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/e2cba0eb
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/e2cba0eb

Branch: refs/heads/master
Commit: e2cba0eb9126753f0b079932ee69effdd3c5a04a
Parents: 6bf70a1
Author: Akash Gupta <ak...@hotmail.com>
Authored: Wed Jun 27 15:01:10 2018 -0700
Committer: Andrew Schwartzmeyer <an...@schwartzmeyer.com>
Committed: Wed Jun 27 15:06:10 2018 -0700

----------------------------------------------------------------------
 3rdparty/libprocess/include/process/io.hpp |  5 +++++
 3rdparty/libprocess/src/CMakeLists.txt     | 22 +++++++++++++++++-----
 2 files changed, 22 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/e2cba0eb/3rdparty/libprocess/include/process/io.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/include/process/io.hpp b/3rdparty/libprocess/include/process/io.hpp
index 5329bfe..468b362 100644
--- a/3rdparty/libprocess/include/process/io.hpp
+++ b/3rdparty/libprocess/include/process/io.hpp
@@ -26,6 +26,7 @@
 namespace process {
 namespace io {
 
+#ifndef ENABLE_LIBWINIO
 /**
  * A possible event while polling.
  *
@@ -37,6 +38,7 @@ const short READ = 0x01;
  * @copydoc process::io::READ
  */
 const short WRITE = 0x02;
+#endif // ENABLE_LIBWINIO
 
 /**
  * Buffered read chunk size.
@@ -69,6 +71,8 @@ Try<Nothing> prepare_async(int_fd fd);
  */
 Try<bool> is_async(int_fd fd);
 
+
+#ifndef ENABLE_LIBWINIO
 /**
  * Returns the events (a subset of the events specified) that can be
  * performed on the specified file descriptor without blocking.
@@ -78,6 +82,7 @@ Try<bool> is_async(int_fd fd);
  */
 // TODO(benh): Add a version which takes multiple file descriptors.
 Future<short> poll(int_fd fd, short events);
+#endif // ENABLE_LIBWINIO
 
 
 /**

http://git-wip-us.apache.org/repos/asf/mesos/blob/e2cba0eb/3rdparty/libprocess/src/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/CMakeLists.txt b/3rdparty/libprocess/src/CMakeLists.txt
index 5f617eb..bb8af6d 100644
--- a/3rdparty/libprocess/src/CMakeLists.txt
+++ b/3rdparty/libprocess/src/CMakeLists.txt
@@ -40,8 +40,6 @@ set(PROCESS_SRC
   metrics/metrics.cpp
   mime.cpp
   pid.cpp
-  posix/io.cpp
-  posix/poll_socket.cpp
   process.cpp
   profiler.cpp
   reap.cpp
@@ -52,8 +50,7 @@ set(PROCESS_SRC
 
 if (WIN32)
   list(APPEND PROCESS_SRC
-    windows/subprocess.cpp
-    windows/libwinio.cpp) # TEMPORARY
+    windows/subprocess.cpp)
 else ()
   list(APPEND PROCESS_SRC
     posix/subprocess.cpp)
@@ -68,12 +65,26 @@ if (ENABLE_LIBEVENT)
   list(APPEND PROCESS_SRC
     posix/libevent/libevent.cpp
     posix/libevent/libevent_poll.cpp)
+elseif (ENABLE_LIBWINIO)
+  list(APPEND PROCESS_SRC
+    windows/event_loop.cpp
+    windows/libwinio.cpp)
 else ()
   list(APPEND PROCESS_SRC
     posix/libev/libev.cpp
     posix/libev/libev_poll.cpp)
 endif ()
 
+if (ENABLE_LIBWINIO)
+  list(APPEND PROCESS_SRC
+    windows/io.cpp
+    windows/poll_socket.cpp)
+else ()
+  list(APPEND PROCESS_SRC
+    posix/io.cpp
+    posix/poll_socket.cpp)
+endif ()
+
 if (ENABLE_SSL)
   list(APPEND PROCESS_SRC
     jwt.cpp
@@ -100,10 +111,11 @@ target_link_libraries(
 target_link_libraries(
   process PRIVATE
   concurrentqueue
-  $<IF:$<BOOL:${ENABLE_LIBEVENT}>,libevent,libev>)
+  $<IF:$<BOOL:${ENABLE_LIBEVENT}>,libevent,$<$<NOT:$<PLATFORM_ID:Windows>>:libev>>)
 
 target_compile_definitions(
   process PRIVATE
+  $<$<BOOL:${ENABLE_LIBWINIO}>:ENABLE_LIBWINIO>
   $<$<BOOL:${ENABLE_LOCK_FREE_RUN_QUEUE}>:LOCK_FREE_RUN_QUEUE>
   $<$<BOOL:${ENABLE_LOCK_FREE_EVENT_QUEUE}>:LOCK_FREE_EVENT_QUEUE>
   $<$<BOOL:${ENABLE_LAST_IN_FIRST_OUT_FIXED_SIZE_SEMAPHORE}>:LAST_IN_FIRST_OUT_FIXED_SIZE_SEMAPHORE>)


[10/16] mesos git commit: Organized POSIX and Windows libprocess implementations.

Posted by an...@apache.org.
Organized POSIX and Windows libprocess implementations.

In preparation of the new Windows IOCP library, the POSIX and Windows
specific files in libprocess have been moved to their own directories
for better code organization.

Review: https://reviews.apache.org/r/67388/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/78244b53
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/78244b53
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/78244b53

Branch: refs/heads/master
Commit: 78244b533a50e775791025c0c2b9d13506c71e60
Parents: 379e56b
Author: Akash Gupta <ak...@hotmail.com>
Authored: Wed Jun 27 14:30:14 2018 -0700
Committer: Andrew Schwartzmeyer <an...@schwartzmeyer.com>
Committed: Wed Jun 27 15:06:10 2018 -0700

----------------------------------------------------------------------
 3rdparty/libprocess/Makefile.am                 |   41 +-
 3rdparty/libprocess/src/CMakeLists.txt          |   18 +-
 3rdparty/libprocess/src/io.cpp                  |  105 +-
 3rdparty/libprocess/src/io_internal.hpp         |   36 +
 3rdparty/libprocess/src/libev.cpp               |  166 ---
 3rdparty/libprocess/src/libev.hpp               |   96 --
 3rdparty/libprocess/src/libev_poll.cpp          |  142 --
 3rdparty/libprocess/src/libevent.cpp            |  211 ---
 3rdparty/libprocess/src/libevent.hpp            |   48 -
 3rdparty/libprocess/src/libevent_poll.cpp       |  112 --
 3rdparty/libprocess/src/libevent_ssl_socket.cpp | 1249 ------------------
 3rdparty/libprocess/src/libevent_ssl_socket.hpp |  198 ---
 3rdparty/libprocess/src/poll_socket.cpp         |  278 ----
 3rdparty/libprocess/src/posix/io.cpp            |  140 ++
 3rdparty/libprocess/src/posix/libev/libev.cpp   |  166 +++
 3rdparty/libprocess/src/posix/libev/libev.hpp   |   96 ++
 .../libprocess/src/posix/libev/libev_poll.cpp   |  142 ++
 .../libprocess/src/posix/libevent/libevent.cpp  |  211 +++
 .../libprocess/src/posix/libevent/libevent.hpp  |   48 +
 .../src/posix/libevent/libevent_poll.cpp        |  112 ++
 .../src/posix/libevent/libevent_ssl_socket.cpp  | 1249 ++++++++++++++++++
 .../src/posix/libevent/libevent_ssl_socket.hpp  |  198 +++
 3rdparty/libprocess/src/posix/poll_socket.cpp   |  278 ++++
 3rdparty/libprocess/src/posix/subprocess.cpp    |  102 ++
 3rdparty/libprocess/src/posix/subprocess.hpp    |  355 +++++
 3rdparty/libprocess/src/socket.cpp              |    2 +-
 3rdparty/libprocess/src/subprocess.cpp          |    4 +-
 3rdparty/libprocess/src/subprocess_posix.cpp    |  102 --
 3rdparty/libprocess/src/subprocess_posix.hpp    |  355 -----
 3rdparty/libprocess/src/subprocess_windows.cpp  |  101 --
 3rdparty/libprocess/src/subprocess_windows.hpp  |  118 --
 3rdparty/libprocess/src/windows/subprocess.cpp  |  101 ++
 3rdparty/libprocess/src/windows/subprocess.hpp  |  118 ++
 33 files changed, 3391 insertions(+), 3307 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/Makefile.am
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/Makefile.am b/3rdparty/libprocess/Makefile.am
index de69d04..2d356aa 100644
--- a/3rdparty/libprocess/Makefile.am
+++ b/3rdparty/libprocess/Makefile.am
@@ -31,7 +31,7 @@ endif
 
 ACLOCAL_AMFLAGS = -I m4
 
-AUTOMAKE_OPTIONS = foreign
+AUTOMAKE_OPTIONS = foreign subdir-objects
 LIBPROCESS_BUILD_DIR=@abs_builddir@
 
 if STANDALONE_LIBPROCESS
@@ -204,6 +204,7 @@ libprocess_la_SOURCES =		\
   src/http_proxy.cpp		\
   src/http_proxy.hpp		\
   src/io.cpp			\
+  src/io_internal.hpp		\
   src/latch.cpp			\
   src/logging.cpp		\
   src/memory_profiler.cpp	\
@@ -211,7 +212,10 @@ libprocess_la_SOURCES =		\
   src/metrics/metrics.cpp	\
   src/mime.cpp			\
   src/pid.cpp			\
-  src/poll_socket.cpp		\
+  src/posix/io.cpp		\
+  src/posix/poll_socket.cpp	\
+  src/posix/subprocess.cpp	\
+  src/posix/subprocess.hpp	\
   src/poll_socket.hpp		\
   src/process.cpp		\
   src/process_reference.hpp	\
@@ -222,18 +226,16 @@ libprocess_la_SOURCES =		\
   src/socket.cpp		\
   src/socket_manager.hpp	\
   src/subprocess.cpp		\
-  src/subprocess_posix.cpp	\
-  src/subprocess_posix.hpp	\
   src/time.cpp
 
 if ENABLE_SSL
-libprocess_la_SOURCES +=	\
-  src/jwt.cpp			\
-  src/jwt_authenticator.cpp	\
-  src/libevent_ssl_socket.cpp	\
-  src/libevent_ssl_socket.hpp	\
-  src/openssl.cpp		\
-  src/openssl.hpp		\
+libprocess_la_SOURCES +=			\
+  src/jwt.cpp					\
+  src/jwt_authenticator.cpp			\
+  src/posix/libevent/libevent_ssl_socket.cpp	\
+  src/posix/libevent/libevent_ssl_socket.hpp	\
+  src/openssl.cpp				\
+  src/openssl.hpp				\
   src/ssl/utilities.cpp
 endif
 
@@ -241,6 +243,7 @@ endif
 libprocess_la_CPPFLAGS =			\
   -DBUILD_DIR=\"$(LIBPROCESS_BUILD_DIR)\"	\
   -I$(srcdir)/include				\
+  -I$(srcdir)/src				\
   $(BOOST_INCLUDE_FLAGS)			\
   $(CONCURRENTQUEUE_INCLUDE_FLAGS)		\
   $(ELFIO_INCLUDE_FLAGS)			\
@@ -260,15 +263,15 @@ libprocess_la_SOURCES +=	\
 endif
 
 if ENABLE_LIBEVENT
-libprocess_la_SOURCES +=	\
-  src/libevent.hpp		\
-  src/libevent.cpp		\
-  src/libevent_poll.cpp
+libprocess_la_SOURCES +=			\
+  src/posix/libevent/libevent.hpp		\
+  src/posix/libevent/libevent.cpp		\
+  src/posix/libevent/libevent_poll.cpp
 else
-libprocess_la_SOURCES +=	\
-  src/libev.hpp			\
-  src/libev.cpp			\
-  src/libev_poll.cpp
+libprocess_la_SOURCES +=			\
+  src/posix/libev/libev.hpp			\
+  src/posix/libev/libev.cpp			\
+  src/posix/libev/libev_poll.cpp
 endif
 
 if ENABLE_STATIC_LIBPROCESS

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/CMakeLists.txt b/3rdparty/libprocess/src/CMakeLists.txt
index 619183e..3544e91 100644
--- a/3rdparty/libprocess/src/CMakeLists.txt
+++ b/3rdparty/libprocess/src/CMakeLists.txt
@@ -40,7 +40,8 @@ set(PROCESS_SRC
   metrics/metrics.cpp
   mime.cpp
   pid.cpp
-  poll_socket.cpp
+  posix/io.cpp
+  posix/poll_socket.cpp
   process.cpp
   profiler.cpp
   reap.cpp
@@ -48,12 +49,13 @@ set(PROCESS_SRC
   subprocess.cpp
   time.cpp)
 
+
 if (WIN32)
   list(APPEND PROCESS_SRC
-    subprocess_windows.cpp)
+    windows/subprocess.cpp)
 else ()
   list(APPEND PROCESS_SRC
-    subprocess_posix.cpp)
+    posix/subprocess.cpp)
 endif ()
 
 if (ENABLE_GRPC)
@@ -63,12 +65,12 @@ endif ()
 
 if (ENABLE_LIBEVENT)
   list(APPEND PROCESS_SRC
-    libevent.cpp
-    libevent_poll.cpp)
+    posix/libevent/libevent.cpp
+    posix/libevent/libevent_poll.cpp)
 else ()
   list(APPEND PROCESS_SRC
-    libev.cpp
-    libev_poll.cpp)
+    posix/libev/libev.cpp
+    posix/libev/libev_poll.cpp)
 endif ()
 
 if (ENABLE_SSL)
@@ -80,7 +82,7 @@ if (ENABLE_SSL)
 
   if (ENABLE_LIBEVENT)
     list(APPEND PROCESS_SRC
-      libevent_ssl_socket.cpp)
+      posix/libevent/libevent_ssl_socket.cpp)
   endif ()
 endif ()
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/io.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/io.cpp b/3rdparty/libprocess/src/io.cpp
index 70715e2..7288d5f 100644
--- a/3rdparty/libprocess/src/io.cpp
+++ b/3rdparty/libprocess/src/io.cpp
@@ -30,120 +30,23 @@
 #include <stout/os/strerror.hpp>
 #include <stout/os/write.hpp>
 
+#include "io_internal.hpp"
+
 using std::string;
 using std::vector;
 
 namespace process {
 namespace io {
-namespace internal {
-
-Future<size_t> read(int_fd fd, void* data, size_t size)
-{
-  // TODO(benh): Let the system calls do what ever they're supposed to
-  // rather than return 0 here?
-  if (size == 0) {
-    return 0;
-  }
-
-  return loop(
-      None(),
-      [=]() -> Future<Option<size_t>> {
-        // Because the file descriptor is non-blocking, we call
-        // read()/recv() immediately. If no data is available than
-        // we'll call `poll` and block. 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).
-        ssize_t length = os::read(fd, data, size);
-        if (length < 0) {
-#ifdef __WINDOWS__
-          WindowsSocketError error;
-#else
-          ErrnoError error;
-#endif // __WINDOWS__
-
-          if (!net::is_restartable_error(error.code) &&
-              !net::is_retryable_error(error.code)) {
-            return Failure(error.message);
-          }
-
-          return None();
-        }
-
-        return length;
-      },
-      [=](const Option<size_t>& length) -> Future<ControlFlow<size_t>> {
-        // Restart/retry if we don't yet have a result.
-        if (length.isNone()) {
-          return io::poll(fd, io::READ)
-            .then([](short event) -> ControlFlow<size_t> {
-              CHECK_EQ(io::READ, event);
-              return Continue();
-            });
-        }
-        return Break(length.get());
-      });
-}
-
-
-Future<size_t> write(int_fd fd, const void* data, size_t size)
-{
-  // TODO(benh): Let the system calls do what ever they're supposed to
-  // rather than return 0 here?
-  if (size == 0) {
-    return 0;
-  }
-
-  return loop(
-      None(),
-      [=]() -> Future<Option<size_t>> {
-        ssize_t length = os::write(fd, data, size);
-
-        if (length < 0) {
-#ifdef __WINDOWS__
-          WindowsSocketError error;
-#else
-          ErrnoError error;
-#endif // __WINDOWS__
-
-          if (!net::is_restartable_error(error.code) &&
-              !net::is_retryable_error(error.code)) {
-            return Failure(error.message);
-          }
-
-          return None();
-        }
-
-        return length;
-      },
-      [=](const Option<size_t>& length) -> Future<ControlFlow<size_t>> {
-        // Restart/retry if we don't yet have a result.
-        if (length.isNone()) {
-          return io::poll(fd, io::WRITE)
-            .then([](short event) -> ControlFlow<size_t> {
-              CHECK_EQ(io::WRITE, event);
-              return Continue();
-            });
-        }
-        return Break(length.get());
-      });
-}
-
-} // namespace internal {
-
 
 Try<Nothing> prepare_async(int_fd fd)
 {
-  // TODO(akagup): Add windows iocp.
-  return os::nonblock(fd);
+  return internal::prepare_async(fd);
 }
 
 
 Try<bool> is_async(int_fd fd)
 {
-  // TODO(akagup): Add windows iocp.
-  return os::isNonblock(fd);
+  return internal::is_async(fd);
 }
 
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/io_internal.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/io_internal.hpp b/3rdparty/libprocess/src/io_internal.hpp
new file mode 100644
index 0000000..09bfa9a
--- /dev/null
+++ b/3rdparty/libprocess/src/io_internal.hpp
@@ -0,0 +1,36 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#ifndef __IO_INTERNAL_HPP__
+#define __IO_INTERNAL_HPP__
+
+#include <process/future.hpp>
+
+#include <stout/os/int_fd.hpp>
+
+namespace process {
+namespace io {
+namespace internal {
+
+Future<size_t> read(int_fd fd, void* data, size_t size);
+
+Future<size_t> write(int_fd fd, const void* data, size_t size);
+
+Try<Nothing> prepare_async(int_fd fd);
+
+Try<bool> is_async(int_fd fd);
+
+} // namespace internal {
+} // namespace io {
+} // namespace process {
+
+#endif // __IO_INTERNAL_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/libev.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/libev.cpp b/3rdparty/libprocess/src/libev.cpp
deleted file mode 100644
index 173ee46..0000000
--- a/3rdparty/libprocess/src/libev.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License
-
-#include <ev.h>
-
-#include <mutex>
-#include <queue>
-
-#include <stout/duration.hpp>
-#include <stout/lambda.hpp>
-#include <stout/nothing.hpp>
-
-#include "event_loop.hpp"
-#include "libev.hpp"
-
-namespace process {
-
-ev_async async_watcher;
-// We need an asynchronous watcher to receive the request to shutdown.
-ev_async shutdown_watcher;
-
-// Define the initial values for all of the declarations made in
-// libev.hpp (since these need to live in the static data space).
-struct ev_loop* loop = nullptr;
-
-std::queue<ev_io*>* watchers = new std::queue<ev_io*>();
-
-std::mutex* watchers_mutex = new std::mutex();
-
-std::queue<lambda::function<void()>>* functions =
-  new std::queue<lambda::function<void()>>();
-
-thread_local bool* _in_event_loop_ = nullptr;
-
-
-void handle_async(struct ev_loop* loop, ev_async* _, int revents)
-{
-  std::queue<lambda::function<void()>> run_functions;
-  synchronized (watchers_mutex) {
-    // Start all the new I/O watchers.
-    while (!watchers->empty()) {
-      ev_io* watcher = watchers->front();
-      watchers->pop();
-      ev_io_start(loop, watcher);
-    }
-
-    // Swap the functions into a temporary queue so that we can invoke
-    // them outside of the mutex.
-    std::swap(run_functions, *functions);
-  }
-
-  // Running the functions outside of the mutex reduces locking
-  // contention as these are arbitrary functions that can take a long
-  // time to execute. Doing this also avoids a deadlock scenario where
-  // (A) mutexes are acquired before calling `run_in_event_loop`,
-  // followed by locking (B) `watchers_mutex`. If we executed the
-  // functions inside the mutex, then the locking order violation
-  // would be this function acquiring the (B) `watchers_mutex`
-  // followed by the arbitrary function acquiring the (A) mutexes.
-  while (!run_functions.empty()) {
-    (run_functions.front())();
-    run_functions.pop();
-  }
-}
-
-
-void handle_shutdown(struct ev_loop* loop, ev_async* _, int revents)
-{
-  ev_unloop(loop, EVUNLOOP_ALL);
-}
-
-
-void EventLoop::initialize()
-{
-  loop = ev_default_loop(EVFLAG_AUTO);
-
-  ev_async_init(&async_watcher, handle_async);
-  ev_async_init(&shutdown_watcher, handle_shutdown);
-
-  ev_async_start(loop, &async_watcher);
-  ev_async_start(loop, &shutdown_watcher);
-}
-
-
-namespace internal {
-
-void handle_delay(struct ev_loop* loop, ev_timer* timer, int revents)
-{
-  lambda::function<void()>* function =
-    reinterpret_cast<lambda::function<void()>*>(timer->data);
-  (*function)();
-  delete function;
-  ev_timer_stop(loop, timer);
-  delete timer;
-}
-
-
-Future<Nothing> delay(
-    const Duration& duration,
-    const lambda::function<void()>& function)
-{
-  ev_timer* timer = new ev_timer();
-  timer->data = reinterpret_cast<void*>(new lambda::function<void()>(function));
-
-  // Determine the 'after' parameter to pass to libev and set it to 0
-  // in the event that it's negative so that we always make sure to
-  // invoke 'function' even if libev doesn't support negative 'after'
-  // values.
-  double after = duration.secs();
-
-  if (after < 0) {
-    after = 0;
-  }
-
-  const double repeat = 0.0;
-
-  ev_timer_init(timer, handle_delay, after, repeat);
-  ev_timer_start(loop, timer);
-
-  return Nothing();
-}
-
-} // namespace internal {
-
-
-void EventLoop::delay(
-    const Duration& duration,
-    const lambda::function<void()>& function)
-{
-  run_in_event_loop<Nothing>(
-      lambda::bind(&internal::delay, duration, function));
-}
-
-
-double EventLoop::time()
-{
-  // TODO(benh): Versus ev_now()?
-  return ev_time();
-}
-
-
-void EventLoop::run()
-{
-  __in_event_loop__ = true;
-
-  ev_loop(loop, 0);
-
-  __in_event_loop__ = false;
-}
-
-
-void EventLoop::stop()
-{
-  ev_async_send(loop, &shutdown_watcher);
-}
-
-} // namespace process {

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/libev.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/libev.hpp b/3rdparty/libprocess/src/libev.hpp
deleted file mode 100644
index d451931..0000000
--- a/3rdparty/libprocess/src/libev.hpp
+++ /dev/null
@@ -1,96 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License
-
-#ifndef __LIBEV_HPP__
-#define __LIBEV_HPP__
-
-#include <ev.h>
-
-#include <mutex>
-#include <queue>
-
-#include <process/future.hpp>
-#include <process/owned.hpp>
-
-#include <stout/lambda.hpp>
-#include <stout/synchronized.hpp>
-
-namespace process {
-
-// Event loop.
-extern struct ev_loop* loop;
-
-// Asynchronous watcher for interrupting loop to specifically deal
-// with IO watchers and functions (via run_in_event_loop).
-extern ev_async async_watcher;
-
-// Queue of I/O watchers to be asynchronously added to the event loop
-// (protected by 'watchers' below).
-// TODO(benh): Replace this queue with functions that we put in
-// 'functions' below that perform the ev_io_start themselves.
-extern std::queue<ev_io*>* watchers;
-extern std::mutex* watchers_mutex;
-
-// Queue of functions to be invoked asynchronously within the vent
-// loop (protected by 'watchers' above).
-extern std::queue<lambda::function<void()>>* functions;
-
-// Per thread bool pointer. We use a pointer to lazily construct the
-// actual bool.
-extern thread_local bool* _in_event_loop_;
-
-#define __in_event_loop__ *(_in_event_loop_ == nullptr ?                \
-  _in_event_loop_ = new bool(false) : _in_event_loop_)
-
-
-// Wrapper around function we want to run in the event loop.
-template <typename T>
-void _run_in_event_loop(
-    const lambda::function<Future<T>()>& f,
-    const Owned<Promise<T>>& promise)
-{
-  // Don't bother running the function if the future has been discarded.
-  if (promise->future().hasDiscard()) {
-    promise->discard();
-  } else {
-    promise->set(f());
-  }
-}
-
-
-// Helper for running a function in the event loop.
-template <typename T>
-Future<T> run_in_event_loop(const lambda::function<Future<T>()>& f)
-{
-  // If this is already the event loop then just run the function.
-  if (__in_event_loop__) {
-    return f();
-  }
-
-  Owned<Promise<T>> promise(new Promise<T>());
-
-  Future<T> future = promise->future();
-
-  // Enqueue the function.
-  synchronized (watchers_mutex) {
-    functions->push(lambda::bind(&_run_in_event_loop<T>, f, promise));
-  }
-
-  // Interrupt the loop.
-  ev_async_send(loop, &async_watcher);
-
-  return future;
-}
-
-} // namespace process {
-
-#endif // __LIBEV_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/libev_poll.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/libev_poll.cpp b/3rdparty/libprocess/src/libev_poll.cpp
deleted file mode 100644
index 96913a6..0000000
--- a/3rdparty/libprocess/src/libev_poll.cpp
+++ /dev/null
@@ -1,142 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License
-
-#include <ev.h>
-
-#include <memory>
-
-#include <process/future.hpp>
-#include <process/process.hpp> // For process::initialize.
-
-#include <stout/lambda.hpp>
-
-#include "libev.hpp"
-
-namespace process {
-
-// Data necessary for polling so we can discard polling and actually
-// stop it in the event loop.
-struct Poll
-{
-  Poll()
-  {
-    // Need to explicitly instantiate the watchers.
-    watcher.io.reset(new ev_io());
-    watcher.async.reset(new ev_async());
-  }
-
-  // An I/O watcher for checking for readability or writeability and
-  // an async watcher for being able to discard the polling.
-  struct {
-    std::shared_ptr<ev_io> io;
-    std::shared_ptr<ev_async> async;
-  } watcher;
-
-  Promise<short> promise;
-};
-
-
-// Event loop callback when I/O is ready on polling file descriptor.
-void polled(struct ev_loop* loop, ev_io* watcher, int revents)
-{
-  Poll* poll = (Poll*) watcher->data;
-
-  ev_io_stop(loop, poll->watcher.io.get());
-
-  // Stop the async watcher (also clears if pending so 'discard_poll'
-  // will not get invoked and we can delete 'poll' here).
-  ev_async_stop(loop, poll->watcher.async.get());
-
-  poll->promise.set(revents);
-
-  delete poll;
-}
-
-
-// Event loop callback when future associated with polling file
-// descriptor has been discarded.
-void discard_poll(struct ev_loop* loop, ev_async* watcher, int revents)
-{
-  Poll* poll = (Poll*) watcher->data;
-
-  // Check and see if we have a pending 'polled' callback and if so
-  // let it "win".
-  if (ev_is_pending(poll->watcher.io.get())) {
-    return;
-  }
-
-  ev_async_stop(loop, poll->watcher.async.get());
-
-  // Stop the I/O watcher (but note we check if pending above) so it
-  // won't get invoked and we can delete 'poll' here.
-  ev_io_stop(loop, poll->watcher.io.get());
-
-  poll->promise.discard();
-
-  delete poll;
-}
-
-
-namespace io {
-namespace internal {
-
-// Helper/continuation of 'poll' on future discard.
-void _poll(const std::shared_ptr<ev_async>& async)
-{
-  ev_async_send(loop, async.get());
-}
-
-
-Future<short> poll(int_fd fd, short events)
-{
-  Poll* poll = new Poll();
-
-  // Have the watchers data point back to the struct.
-  poll->watcher.async->data = poll;
-  poll->watcher.io->data = poll;
-
-  // Get a copy of the future to avoid any races with the event loop.
-  Future<short> future = poll->promise.future();
-
-  // Initialize and start the async watcher.
-  ev_async_init(poll->watcher.async.get(), discard_poll);
-  ev_async_start(loop, poll->watcher.async.get());
-
-  // Make sure we stop polling if a discard occurs on our future.
-  // Note that it's possible that we'll invoke '_poll' when someone
-  // does a discard even after the polling has already completed, but
-  // in this case while we will interrupt the event loop since the
-  // async watcher has already been stopped we won't cause
-  // 'discard_poll' to get invoked.
-  future.onDiscard(lambda::bind(&_poll, poll->watcher.async));
-
-  // Initialize and start the I/O watcher.
-  ev_io_init(poll->watcher.io.get(), polled, fd, events);
-  ev_io_start(loop, poll->watcher.io.get());
-
-  return future;
-}
-
-} // namespace internal {
-
-
-Future<short> poll(int_fd fd, short events)
-{
-  process::initialize();
-
-  // TODO(benh): Check if the file descriptor is non-blocking?
-
-  return run_in_event_loop<short>(lambda::bind(&internal::poll, fd, events));
-}
-
-} // namespace io {
-} // namespace process {

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/libevent.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/libevent.cpp b/3rdparty/libprocess/src/libevent.cpp
deleted file mode 100644
index fb595bc..0000000
--- a/3rdparty/libprocess/src/libevent.cpp
+++ /dev/null
@@ -1,211 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License
-
-#ifndef __WINDOWS__
-#include <unistd.h>
-#endif // __WINDOWS__
-
-#include <mutex>
-
-#include <event2/event.h>
-#include <event2/thread.h>
-#include <event2/util.h>
-
-#include <process/logging.hpp>
-#include <process/once.hpp>
-
-#include <stout/synchronized.hpp>
-
-#include "event_loop.hpp"
-#include "libevent.hpp"
-
-namespace process {
-
-event_base* base = nullptr;
-
-
-static std::mutex* functions_mutex = new std::mutex();
-std::queue<lambda::function<void()>>* functions =
-  new std::queue<lambda::function<void()>>();
-
-
-thread_local bool* _in_event_loop_ = nullptr;
-
-
-void async_function(evutil_socket_t socket, short which, void* arg)
-{
-  event* ev = reinterpret_cast<event*>(arg);
-  event_free(ev);
-
-  std::queue<lambda::function<void()>> q;
-
-  synchronized (functions_mutex) {
-    std::swap(q, *functions);
-  }
-
-  while (!q.empty()) {
-    q.front()();
-    q.pop();
-  }
-}
-
-
-void run_in_event_loop(
-    const lambda::function<void()>& f,
-    EventLoopLogicFlow event_loop_logic_flow)
-{
-  if (__in_event_loop__ && event_loop_logic_flow == ALLOW_SHORT_CIRCUIT) {
-    f();
-    return;
-  }
-
-  synchronized (functions_mutex) {
-    functions->push(f);
-
-    // Add an event and activate it to interrupt the event loop.
-    // TODO(jmlvanre): after libevent v 2.1 we can use
-    // event_self_cbarg instead of re-assigning the event. For now we
-    // manually re-assign the event to pass in the pointer to the
-    // event itself as the callback argument.
-    event* ev = evtimer_new(base, async_function, nullptr);
-
-    // 'event_assign' is only valid on non-pending AND non-active
-    // events. This means we have to assign the callback before
-    // calling 'event_active'.
-    if (evtimer_assign(ev, base, async_function, ev) < 0) {
-      LOG(FATAL) << "Failed to assign callback on event";
-    }
-
-    event_active(ev, EV_TIMEOUT, 0);
-  }
-}
-
-
-void EventLoop::run()
-{
-  __in_event_loop__ = true;
-
-  do {
-    int result = event_base_loop(base, EVLOOP_ONCE);
-    if (result < 0) {
-      LOG(FATAL) << "Failed to run event loop";
-    } else if (result > 0) {
-      // All events are handled, continue event loop.
-      continue;
-    } else {
-      CHECK_EQ(0, result);
-      if (event_base_got_break(base)) {
-        break;
-      } else if (event_base_got_exit(base)) {
-        break;
-      }
-    }
-  } while (true);
-
-  __in_event_loop__ = false;
-}
-
-
-void EventLoop::stop()
-{
-  event_base_loopexit(base, nullptr);
-}
-
-
-namespace internal {
-
-struct Delay
-{
-  lambda::function<void()> function;
-  event* timer;
-};
-
-void handle_delay(evutil_socket_t, short, void* arg)
-{
-  Delay* delay = reinterpret_cast<Delay*>(arg);
-  delay->function();
-  event_free(delay->timer);
-  delete delay;
-}
-
-}  // namespace internal {
-
-
-void EventLoop::delay(
-    const Duration& duration,
-    const lambda::function<void()>& function)
-{
-  internal::Delay* delay = new internal::Delay();
-  delay->timer = evtimer_new(base, &internal::handle_delay, delay);
-  if (delay->timer == nullptr) {
-    LOG(FATAL) << "Failed to delay, evtimer_new";
-  }
-
-  delay->function = function;
-
-  timeval t{0, 0};
-  if (duration > Seconds(0)) {
-    t = duration.timeval();
-  }
-
-  evtimer_add(delay->timer, &t);
-}
-
-
-double EventLoop::time()
-{
-  // We explicitly call `evutil_gettimeofday()` for now to avoid any
-  // issues that may be introduced by using the cached value provided
-  // by `event_base_gettimeofday_cached()`. Since a lot of logic in
-  // libprocess depends on time math, we want to log fatal rather than
-  // cause logic errors if the time fails.
-  timeval t;
-  if (evutil_gettimeofday(&t, nullptr) < 0) {
-    LOG(FATAL) << "Failed to get time, evutil_gettimeofday";
-  }
-
-  return Duration(t).secs();
-}
-
-
-void EventLoop::initialize()
-{
-  static Once* initialized = new Once();
-
-  if (initialized->once()) {
-    return;
-  }
-
-  // We need to initialize Libevent differently depending on the
-  // operating system threading support.
-#if defined(EVTHREAD_USE_PTHREADS_IMPLEMENTED)
-  if (evthread_use_pthreads() < 0) {
-    LOG(FATAL) << "Failed to initialize, evthread_use_pthreads";
-  }
-#elif defined(EVTHREAD_USE_WINDOWS_THREADS_IMPLEMENTED)
-  if (evthread_use_windows_threads() < 0) {
-    LOG(FATAL) << "Failed to initialize, evthread_use_windows_threads";
-  }
-#else
-#error "Libevent must be compiled with either pthread or Windows thread support"
-#endif
-
-  base = event_base_new();
-
-  if (base == nullptr) {
-    LOG(FATAL) << "Failed to initialize, event_base_new";
-  }
-
-  initialized->done();
-}
-
-} // namespace process {

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/libevent.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/libevent.hpp b/3rdparty/libprocess/src/libevent.hpp
deleted file mode 100644
index 2eb9790..0000000
--- a/3rdparty/libprocess/src/libevent.hpp
+++ /dev/null
@@ -1,48 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License
-
-#ifndef __LIBEVENT_HPP__
-#define __LIBEVENT_HPP__
-
-#include <event2/event.h>
-
-#include <stout/lambda.hpp>
-
-namespace process {
-
-// Event loop.
-extern event_base* base;
-
-
-// Per thread bool pointer. We use a pointer to lazily construct the
-// actual bool.
-extern thread_local bool* _in_event_loop_;
-
-
-#define __in_event_loop__ *(_in_event_loop_ == nullptr ?                \
-  _in_event_loop_ = new bool(false) : _in_event_loop_)
-
-
-enum EventLoopLogicFlow
-{
-  ALLOW_SHORT_CIRCUIT,
-  DISALLOW_SHORT_CIRCUIT
-};
-
-
-void run_in_event_loop(
-    const lambda::function<void()>& f,
-    EventLoopLogicFlow event_loop_logic_flow = ALLOW_SHORT_CIRCUIT);
-
-} // namespace process {
-
-#endif // __LIBEVENT_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/libevent_poll.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/libevent_poll.cpp b/3rdparty/libprocess/src/libevent_poll.cpp
deleted file mode 100644
index 038dde2..0000000
--- a/3rdparty/libprocess/src/libevent_poll.cpp
+++ /dev/null
@@ -1,112 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License
-
-#include <event2/event.h>
-
-#include <memory>
-
-#include <process/future.hpp>
-#include <process/io.hpp>
-#include <process/process.hpp> // For process::initialize.
-
-#include "libevent.hpp"
-
-namespace process {
-
-namespace io {
-namespace internal {
-
-struct Poll
-{
-  Promise<short> promise;
-  std::shared_ptr<event> ev;
-};
-
-
-void pollCallback(evutil_socket_t, short what, void* arg)
-{
-  Poll* poll = reinterpret_cast<Poll*>(arg);
-
-  if (poll->promise.future().hasDiscard()) {
-    poll->promise.discard();
-  } else {
-    // Convert libevent specific EV_READ / EV_WRITE to io::* specific
-    // values of these enumerations.
-    short events =
-      ((what & EV_READ) ? io::READ : 0) | ((what & EV_WRITE) ? io::WRITE : 0);
-
-    poll->promise.set(events);
-  }
-
-  // Deleting the `poll` also destructs `ev` and hence triggers `event_free`,
-  // which makes the event non-pending.
-  delete poll;
-}
-
-
-void pollDiscard(const std::weak_ptr<event>& ev, short events)
-{
-  // Discarding inside the event loop prevents `pollCallback()` from being
-  // called twice if the future is discarded.
-  run_in_event_loop([=]() {
-    std::shared_ptr<event> shared = ev.lock();
-    // If `ev` cannot be locked `pollCallback` already ran. If it was locked
-    // but not pending, `pollCallback` is scheduled to be executed.
-    if (static_cast<bool>(shared) &&
-        event_pending(shared.get(), events, nullptr)) {
-      // `event_active` will trigger the `pollCallback` to be executed.
-      event_active(shared.get(), EV_READ, 0);
-    }
-  });
-}
-
-} // namespace internal {
-
-
-Future<short> poll(int_fd fd, short events)
-{
-  process::initialize();
-
-  internal::Poll* poll = new internal::Poll();
-
-  Future<short> future = poll->promise.future();
-
-  // Convert io::READ / io::WRITE to libevent specific values of these
-  // enumerations.
-  short what =
-    ((events & io::READ) ? EV_READ : 0) | ((events & io::WRITE) ? EV_WRITE : 0);
-
-  // Bind `event_free` to the destructor of the `ev` shared pointer
-  // guaranteeing that the event will be freed only once.
-  poll->ev.reset(
-      event_new(base, fd, what, &internal::pollCallback, poll),
-      event_free);
-
-  if (poll->ev == nullptr) {
-    LOG(FATAL) << "Failed to poll, event_new";
-  }
-
-  // Using a `weak_ptr` prevents `ev` to become a dangling pointer if
-  // the returned future is discarded after the event is triggered.
-  // The `weak_ptr` needs to be created before `event_add` in case
-  // the event is ready and the callback is executed before creating
-  // `ev`.
-  std::weak_ptr<event> ev(poll->ev);
-
-  event_add(poll->ev.get(), nullptr);
-
-  return future
-    .onDiscard(lambda::bind(&internal::pollDiscard, ev, what));
-}
-
-} // namespace io {
-} // namespace process {

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/libevent_ssl_socket.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/libevent_ssl_socket.cpp b/3rdparty/libprocess/src/libevent_ssl_socket.cpp
deleted file mode 100644
index 436b389..0000000
--- a/3rdparty/libprocess/src/libevent_ssl_socket.cpp
+++ /dev/null
@@ -1,1249 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License
-
-#include <event2/buffer.h>
-#include <event2/bufferevent_ssl.h>
-#include <event2/event.h>
-#include <event2/listener.h>
-#include <event2/thread.h>
-#include <event2/util.h>
-
-#include <openssl/ssl.h>
-#include <openssl/err.h>
-
-#include <process/queue.hpp>
-#include <process/socket.hpp>
-
-#include <process/ssl/flags.hpp>
-
-#include <stout/net.hpp>
-#include <stout/synchronized.hpp>
-
-#include <stout/os/close.hpp>
-#include <stout/os/dup.hpp>
-#include <stout/os/fcntl.hpp>
-
-#include "libevent.hpp"
-#include "libevent_ssl_socket.hpp"
-#include "openssl.hpp"
-#include "poll_socket.hpp"
-
-// Locking:
-//
-// We use the BEV_OPT_THREADSAFE flag when constructing bufferevents
-// so that all **functions that are called from the event loop that
-// take a bufferevent as a parameter will automatically have the
-// lock acquired**.
-//
-// This means that everywhere that the libevent library does not
-// already lock the bev, we need to manually 'synchronize (bev) {'.
-// To further complicate matters, due to a deadlock scneario in
-// libevent-openssl (v 2.0.21) we currently modify bufferevents using
-// continuations in the event loop, but these functions, while run
-// from within the event loop, are not passed the 'bev' as a parameter
-// and thus MUST use 'synchronized (bev)'. See 'Continuation' comment
-// below for more details on why we need to invoke these continuations
-// from within the event loop.
-
-// Continuations via 'run_in_event_loop(...)':
-//
-// There is a deadlock scenario in libevent-openssl (v 2.0.21) when
-// modifying the bufferevent (bev) from another thread (not the event
-// loop). To avoid this we run all bufferevent manipulation logic in
-// continuations that are executed within the event loop.
-
-// DISALLOW_SHORT_CIRCUIT:
-//
-// We disallow short-circuiting in 'run_in_event_loop' due to a bug in
-// libevent_openssl with deferred callbacks still being called (still
-// in the run queue) even though a bev has been disabled.
-
-using std::queue;
-using std::string;
-
-// Specialization of 'synchronize' to use bufferevent with the
-// 'synchronized' macro.
-static Synchronized<bufferevent> synchronize(bufferevent* bev)
-{
-  return Synchronized<bufferevent>(
-      bev,
-      [](bufferevent* bev) { bufferevent_lock(bev); },
-      [](bufferevent* bev) { bufferevent_unlock(bev); });
-}
-
-namespace process {
-namespace network {
-namespace internal {
-
-Try<std::shared_ptr<SocketImpl>> LibeventSSLSocketImpl::create(int_fd s)
-{
-  openssl::initialize();
-
-  if (!openssl::flags().enabled) {
-    return Error("SSL is disabled");
-  }
-
-  auto socket = std::make_shared<LibeventSSLSocketImpl>(s);
-  // See comment at 'initialize' declaration for why we call this.
-  socket->initialize();
-  return socket;
-}
-
-
-LibeventSSLSocketImpl::~LibeventSSLSocketImpl()
-{
-  // We defer termination and destruction of all event loop specific
-  // calls and structures. This is a safety against the socket being
-  // destroyed before existing event loop calls have completed since
-  // they require valid data structures (the weak pointer).
-  //
-  // Release ownership of the file descriptor so that
-  // we can defer closing the socket.
-  int_fd fd = release();
-  CHECK(fd >= 0);
-
-  evconnlistener* _listener = listener;
-  bufferevent* _bev = bev;
-  std::weak_ptr<LibeventSSLSocketImpl>* _event_loop_handle = event_loop_handle;
-
-  run_in_event_loop(
-      [_listener, _bev, _event_loop_handle, fd]() {
-        // Once this lambda is called, it should not be possible for
-        // more event loop callbacks to be triggered with 'this->bev'.
-        // This is important because we delete event_loop_handle which
-        // is the callback argument for any event loop callbacks.
-        // This lambda is responsible for ensuring 'this->bev' is
-        // disabled, and cleaning up any remaining state associated
-        // with the event loop.
-
-        CHECK(__in_event_loop__);
-
-        if (_listener != nullptr) {
-          evconnlistener_free(_listener);
-        }
-
-        if (_bev != nullptr) {
-          // NOTE: Removes all future callbacks using 'this->bev'.
-          bufferevent_disable(_bev, EV_READ | EV_WRITE);
-
-          SSL* ssl = bufferevent_openssl_get_ssl(_bev);
-          SSL_free(ssl);
-          bufferevent_free(_bev);
-        }
-
-        CHECK_SOME(os::close(fd)) << "Failed to close socket";
-
-        delete _event_loop_handle;
-      },
-      DISALLOW_SHORT_CIRCUIT);
-}
-
-
-void LibeventSSLSocketImpl::initialize()
-{
-  event_loop_handle = new std::weak_ptr<LibeventSSLSocketImpl>(shared(this));
-}
-
-
-Try<Nothing, SocketError> LibeventSSLSocketImpl::shutdown(int how)
-{
-  // Nothing to do if this socket was never initialized.
-  synchronized (lock) {
-    if (bev == nullptr) {
-      // If it was not initialized, then there should also be no
-      // requests.
-      CHECK(connect_request.get() == nullptr);
-      CHECK(recv_request.get() == nullptr);
-      CHECK(send_request.get() == nullptr);
-
-      // We expect this to fail and generate an 'ENOTCONN' failure as
-      // no connection should exist at this point.
-      if (::shutdown(s, how) < 0) {
-        return SocketError();
-      }
-
-      return Nothing();
-    }
-  }
-
-  // Extend the life-time of 'this' through the execution of the
-  // lambda in the event loop. Note: The 'self' needs to be explicitly
-  // captured because we're not using it in the body of the lambda. We
-  // can use a 'shared_ptr' because run_in_event_loop is guaranteed to
-  // execute.
-  auto self = shared(this);
-
-  run_in_event_loop(
-      [self]() {
-        CHECK(__in_event_loop__);
-        CHECK(self);
-
-        CHECK_NOTNULL(self->bev);
-
-        synchronized (self->bev) {
-          Owned<RecvRequest> request;
-
-          // Swap the 'recv_request' under the object lock.
-          synchronized (self->lock) {
-            std::swap(request, self->recv_request);
-          }
-
-          // If there is still a pending receive request then close it.
-          if (request.get() != nullptr) {
-            request->promise
-              .set(bufferevent_read(self->bev, request->data, request->size));
-          }
-
-          // Workaround for SSL shutdown, see http://www.wangafu.net/~nickm/libevent-book/Ref6a_advanced_bufferevents.html // NOLINT
-          SSL* ssl = bufferevent_openssl_get_ssl(self->bev);
-          SSL_set_shutdown(ssl, SSL_RECEIVED_SHUTDOWN);
-          SSL_shutdown(ssl);
-        }
-      },
-      DISALLOW_SHORT_CIRCUIT);
-
-  return Nothing();
-}
-
-
-// Only runs in event loop. No locks required. See 'Locking' note at
-// top of file.
-void LibeventSSLSocketImpl::recv_callback(bufferevent* /*bev*/, void* arg)
-{
-  CHECK(__in_event_loop__);
-
-  std::weak_ptr<LibeventSSLSocketImpl>* handle =
-    reinterpret_cast<std::weak_ptr<LibeventSSLSocketImpl>*>(CHECK_NOTNULL(arg));
-
-  std::shared_ptr<LibeventSSLSocketImpl> impl(handle->lock());
-
-  // Don't call the 'recv_callback' unless the socket is still valid.
-  if (impl != nullptr) {
-    impl->recv_callback();
-  }
-}
-
-
-// Only runs in event loop. Member function continuation of static
-// 'recv_callback'. This function can be called from two places -
-// a) `LibeventSSLSocketImpl::recv` when a new Socket::recv is called and there
-//    is buffer available to read.
-// b) `LibeventSSLSocketImpl::recv_callback when libevent calls the deferred
-//    read callback.
-void LibeventSSLSocketImpl::recv_callback()
-{
-  CHECK(__in_event_loop__);
-
-  Owned<RecvRequest> request;
-
-  const size_t buffer_length = evbuffer_get_length(bufferevent_get_input(bev));
-
-  // Swap out the request object IFF there is buffer available to read. We check
-  // this here because it is possible that when the libevent deferred callback
-  // was called, a Socket::recv context already read the buffer from the event.
-  // Following sequence is possible:
-  // a. libevent finds a buffer ready to be read.
-  // b. libevent queues buffer event to be dispatched.
-  // c. Socket::recv is called that creates a new request.
-  // d. Socket::recv finds buffer length > 0.
-  // e. Socket::recv reads the buffer.
-  // f. A new request Socket::recv is called which creates a new request.
-  // g. libevent callback is called for the event queued at step b.
-  // h. libevent callback finds the length of the buffer as 0 but the request is
-  //    a non-nullptr due to step f.
-  if (buffer_length > 0 || received_eof) {
-    synchronized (lock) {
-      std::swap(request, recv_request);
-    }
-  }
-
-  if (request.get() != nullptr) {
-    if (buffer_length > 0) {
-      size_t length = bufferevent_read(bev, request->data, request->size);
-      CHECK(length > 0);
-
-      request->promise.set(length);
-    } else {
-      CHECK(received_eof);
-      request->promise.set(0);
-    }
-  }
-}
-
-
-// Only runs in event loop. No locks required. See 'Locking' note at
-// top of file.
-void LibeventSSLSocketImpl::send_callback(bufferevent* /*bev*/, void* arg)
-{
-  CHECK(__in_event_loop__);
-
-  std::weak_ptr<LibeventSSLSocketImpl>* handle =
-    reinterpret_cast<std::weak_ptr<LibeventSSLSocketImpl>*>(CHECK_NOTNULL(arg));
-
-  std::shared_ptr<LibeventSSLSocketImpl> impl(handle->lock());
-
-  // Don't call the 'send_callback' unless the socket is still valid.
-  if (impl != nullptr) {
-    impl->send_callback();
-  }
-}
-
-
-// Only runs in event loop. Member function continuation of static
-// 'recv_callback'.
-void LibeventSSLSocketImpl::send_callback()
-{
-  CHECK(__in_event_loop__);
-
-  Owned<SendRequest> request;
-
-  synchronized (lock) {
-    std::swap(request, send_request);
-  }
-
-  if (request.get() != nullptr) {
-    request->promise.set(request->size);
-  }
-}
-
-
-// Only runs in event loop. No locks required. See 'Locking' note at
-// top of file.
-void LibeventSSLSocketImpl::event_callback(
-    bufferevent* /*bev*/,
-    short events,
-    void* arg)
-{
-  CHECK(__in_event_loop__);
-
-  std::weak_ptr<LibeventSSLSocketImpl>* handle =
-    reinterpret_cast<std::weak_ptr<LibeventSSLSocketImpl>*>(CHECK_NOTNULL(arg));
-
-  std::shared_ptr<LibeventSSLSocketImpl> impl(handle->lock());
-
-  // Don't call the 'event_callback' unless the socket is still valid.
-  if (impl != nullptr) {
-    impl->event_callback(events);
-  }
-}
-
-
-// Only runs in event loop. Member function continuation of static
-// 'recv_callback'.
-void LibeventSSLSocketImpl::event_callback(short events)
-{
-  CHECK(__in_event_loop__);
-
-  // TODO(bmahler): Libevent's invariant is that `events` contains:
-  //
-  //   (1) one of BEV_EVENT_READING or BEV_EVENT_WRITING to
-  //       indicate whether the event was on the read or write path.
-  //
-  //   (2) one of BEV_EVENT_EOF, BEV_EVENT_ERROR, BEV_EVENT_TIMEOUT,
-  //       BEV_EVENT_CONNECTED.
-  //
-  // (1) allows us to handle read and write errors separately.
-  // HOWEVER, for SSL bufferevents in 2.0.x, libevent never seems
-  // to tell us about BEV_EVENT_READING or BEV_EVENT_WRITING,
-  // which forces us to write incorrect logic here by treating all
-  // events as affecting both reads and writes.
-  //
-  // This has been fixed in 2.1.x:
-  //   2.1 "What's New":
-  //     https://github.com/libevent/libevent/blob/release-2.1.8-stable/whatsnew-2.1.txt#L333-L335 // NOLINT
-  //   Commit:
-  //     https://github.com/libevent/libevent/commit/f7eb69ace
-  //
-  // We should require 2.1.x so that we can correctly distinguish
-  // between the read and write errors, and not have two code paths
-  // depending on the libevent version, see MESOS-5999, MESOS-6770.
-
-  Owned<RecvRequest> current_recv_request;
-  Owned<SendRequest> current_send_request;
-  Owned<ConnectRequest> current_connect_request;
-
-  if (events & BEV_EVENT_EOF ||
-      events & BEV_EVENT_CONNECTED ||
-      events & BEV_EVENT_ERROR) {
-    synchronized (lock) {
-      std::swap(current_recv_request, recv_request);
-      std::swap(current_send_request, send_request);
-      std::swap(current_connect_request, connect_request);
-    }
-  }
-
-  // First handle EOF, we also look for `BEV_EVENT_ERROR` with
-  // `EVUTIL_SOCKET_ERROR() == 0` since this occurs as a result
-  // of a "dirty" SSL shutdown (i.e. TCP close before SSL close)
-  // or when this socket has been shut down and further sends
-  // are performed.
-  //
-  // TODO(bmahler): We don't expose "dirty" SSL shutdowns as
-  // recv errors, but perhaps we should?
-  if (events & BEV_EVENT_EOF ||
-     (events & BEV_EVENT_ERROR && EVUTIL_SOCKET_ERROR() == 0)) {
-    received_eof = true;
-
-    if (current_recv_request.get() != nullptr) {
-      // Drain any remaining data from the bufferevent or complete the
-      // promise with 0 to signify EOF. Because we set `received_eof`,
-      // subsequent calls to `recv` will return 0 if there is no data
-      // remaining on the buffer.
-      if (evbuffer_get_length(bufferevent_get_input(bev)) > 0) {
-        size_t length =
-          bufferevent_read(
-              bev,
-              current_recv_request->data,
-              current_recv_request->size);
-        CHECK(length > 0);
-
-        current_recv_request->promise.set(length);
-      } else {
-        current_recv_request->promise.set(0);
-      }
-    }
-
-    if (current_send_request.get() != nullptr) {
-      current_send_request->promise.fail("Failed send: connection closed");
-    }
-
-    if (current_connect_request.get() != nullptr) {
-      SSL* ssl = bufferevent_openssl_get_ssl(CHECK_NOTNULL(bev));
-      SSL_free(ssl);
-      bufferevent_free(CHECK_NOTNULL(bev));
-      bev = nullptr;
-      current_connect_request->promise.fail(
-          "Failed connect: connection closed");
-    }
-  } else if (events & BEV_EVENT_CONNECTED) {
-    // We should not have receiving or sending request while still
-    // connecting.
-    CHECK(current_recv_request.get() == nullptr);
-    CHECK(current_send_request.get() == nullptr);
-    CHECK_NOTNULL(current_connect_request.get());
-
-    // If we're connecting, then we've succeeded. Time to do
-    // post-verification.
-    CHECK_NOTNULL(bev);
-
-    // Do post-validation of connection.
-    SSL* ssl = bufferevent_openssl_get_ssl(bev);
-
-    Try<Nothing> verify = openssl::verify(ssl, peer_hostname, peer_ip);
-    if (verify.isError()) {
-      VLOG(1) << "Failed connect, verification error: " << verify.error();
-      SSL_free(ssl);
-      bufferevent_free(bev);
-      bev = nullptr;
-      current_connect_request->promise.fail(verify.error());
-      return;
-    }
-
-    current_connect_request->promise.set(Nothing());
-  } else if (events & BEV_EVENT_ERROR) {
-    CHECK(EVUTIL_SOCKET_ERROR() != 0);
-    std::ostringstream error_stream;
-    error_stream << evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR());
-
-    // If there is a valid error, fail any requests and log the error.
-    VLOG(1) << "Socket error: " << error_stream.str();
-
-    if (current_recv_request.get() != nullptr) {
-      current_recv_request->promise.fail(
-          "Failed recv, connection error: " +
-          error_stream.str());
-    }
-
-    if (current_send_request.get() != nullptr) {
-      current_send_request->promise.fail(
-          "Failed send, connection error: " +
-          error_stream.str());
-    }
-
-    if (current_connect_request.get() != nullptr) {
-      SSL* ssl = bufferevent_openssl_get_ssl(CHECK_NOTNULL(bev));
-      SSL_free(ssl);
-      bufferevent_free(CHECK_NOTNULL(bev));
-      bev = nullptr;
-      current_connect_request->promise.fail(
-          "Failed connect, connection error: " +
-          error_stream.str());
-    }
-  }
-}
-
-
-LibeventSSLSocketImpl::LibeventSSLSocketImpl(int_fd _s)
-  : SocketImpl(_s),
-    bev(nullptr),
-    listener(nullptr),
-    recv_request(nullptr),
-    send_request(nullptr),
-    connect_request(nullptr),
-    event_loop_handle(nullptr) {}
-
-
-LibeventSSLSocketImpl::LibeventSSLSocketImpl(
-    int_fd _s,
-    bufferevent* _bev,
-    Option<string>&& _peer_hostname)
-  : SocketImpl(_s),
-    bev(_bev),
-    listener(nullptr),
-    recv_request(nullptr),
-    send_request(nullptr),
-    connect_request(nullptr),
-    event_loop_handle(nullptr),
-    peer_hostname(std::move(_peer_hostname)) {}
-
-
-Future<Nothing> LibeventSSLSocketImpl::connect(const Address& address)
-{
-  if (bev != nullptr) {
-    return Failure("Socket is already connected");
-  }
-
-  if (connect_request.get() != nullptr) {
-    return Failure("Socket is already connecting");
-  }
-
-  SSL* ssl = SSL_new(openssl::context());
-  if (ssl == nullptr) {
-    return Failure("Failed to connect: SSL_new");
-  }
-
-  // Construct the bufferevent in the connecting state.
-  // We set 'BEV_OPT_DEFER_CALLBACKS' to avoid calling the
-  // 'event_callback' before 'bufferevent_socket_connect' returns.
-  CHECK(bev == nullptr);
-  bev = bufferevent_openssl_socket_new(
-      base,
-      s,
-      ssl,
-      BUFFEREVENT_SSL_CONNECTING,
-      BEV_OPT_THREADSAFE | BEV_OPT_DEFER_CALLBACKS);
-
-  if (bev == nullptr) {
-    // We need to free 'ssl' here because the bev won't clean it up
-    // for us.
-    SSL_free(ssl);
-    return Failure("Failed to connect: bufferevent_openssl_socket_new");
-  }
-
-  if (address.family() == Address::Family::INET4 ||
-      address.family() == Address::Family::INET6) {
-    // Try and determine the 'peer_hostname' from the address we're
-    // connecting to in order to properly verify the certificate
-    // later.
-    const Try<string> hostname =
-      network::convert<inet::Address>(address)->hostname();
-
-    if (hostname.isError()) {
-      VLOG(2) << "Could not determine hostname of peer: " << hostname.error();
-    } else {
-      VLOG(2) << "Connecting to " << hostname.get();
-      peer_hostname = hostname.get();
-    }
-
-    // Determine the 'peer_ip' from the address we're connecting to in
-    // order to properly verify the certificate later.
-    peer_ip = network::convert<inet::Address>(address)->ip;
-  }
-
-  // Optimistically construct a 'ConnectRequest' and future.
-  Owned<ConnectRequest> request(new ConnectRequest());
-  Future<Nothing> future = request->promise.future();
-
-  // Assign 'connect_request' under lock, fail on error.
-  synchronized (lock) {
-    if (connect_request.get() != nullptr) {
-      SSL_free(ssl);
-      bufferevent_free(bev);
-      bev = nullptr;
-      return Failure("Socket is already connecting");
-    }
-    std::swap(request, connect_request);
-  }
-
-  // Extend the life-time of 'this' through the execution of the
-  // lambda in the event loop. Note: The 'self' needs to be explicitly
-  // captured because we're not using it in the body of the lambda. We
-  // can use a 'shared_ptr' because run_in_event_loop is guaranteed to
-  // execute.
-  auto self = shared(this);
-
-  run_in_event_loop(
-      [self, address]() {
-        sockaddr_storage addr = address;
-
-          // Assign the callbacks for the bufferevent. We do this
-          // before the 'bufferevent_socket_connect()' call to avoid
-          // any race on the underlying buffer events becoming ready.
-          bufferevent_setcb(
-              self->bev,
-              &LibeventSSLSocketImpl::recv_callback,
-              &LibeventSSLSocketImpl::send_callback,
-              &LibeventSSLSocketImpl::event_callback,
-              CHECK_NOTNULL(self->event_loop_handle));
-
-          if (bufferevent_socket_connect(
-                  self->bev,
-                  reinterpret_cast<sockaddr*>(&addr),
-                  address.size()) < 0) {
-            SSL* ssl = bufferevent_openssl_get_ssl(CHECK_NOTNULL(self->bev));
-            SSL_free(ssl);
-            bufferevent_free(self->bev);
-            self->bev = nullptr;
-
-            Owned<ConnectRequest> request;
-
-            // Swap out the 'connect_request' so we can destroy it
-            // outside of the lock.
-            synchronized (self->lock) {
-              std::swap(request, self->connect_request);
-            }
-
-            CHECK_NOTNULL(request.get());
-
-            // Fail the promise since we failed to connect.
-            request->promise.fail(
-                "Failed to connect: bufferevent_socket_connect");
-          }
-      },
-      DISALLOW_SHORT_CIRCUIT);
-
-  return future;
-}
-
-
-Future<size_t> LibeventSSLSocketImpl::recv(char* data, size_t size)
-{
-  // Optimistically construct a 'RecvRequest' and future.
-  Owned<RecvRequest> request(new RecvRequest(data, size));
-  std::weak_ptr<LibeventSSLSocketImpl> weak_self(shared(this));
-
-  // If the user of the future decides to 'discard', then we want to
-  // test whether the request was already satisfied.
-  // We capture a 'weak_ptr' to 'this' (as opposed to a 'shared_ptr')
-  // because the socket could be destroyed before this lambda is
-  // executed. If we used a 'shared_ptr' then this lambda could extend
-  // the life-time of 'this' unnecessarily.
-  Future<size_t> future = request->promise.future()
-    .onDiscard([weak_self]() {
-      // Extend the life-time of 'this' through the execution of the
-      // lambda in the event loop. Note: The 'self' needs to be
-      // explicitly captured because we're not using it in the body of
-      // the lambda. We can use a 'shared_ptr' because
-      // run_in_event_loop is guaranteed to execute.
-      std::shared_ptr<LibeventSSLSocketImpl> self(weak_self.lock());
-
-      if (self != nullptr) {
-        run_in_event_loop(
-            [self]() {
-              CHECK(__in_event_loop__);
-              CHECK(self);
-
-              Owned<RecvRequest> request;
-
-              synchronized (self->lock) {
-                std::swap(request, self->recv_request);
-              }
-
-              // Only discard if the request hasn't already been
-              // satisfied.
-              if (request.get() != nullptr) {
-                // Discard the promise outside of the object lock as
-                // the callbacks can be expensive.
-                request->promise.discard();
-              }
-            },
-            DISALLOW_SHORT_CIRCUIT);
-      }
-    });
-
-  // Assign 'recv_request' under lock, fail on error.
-  synchronized (lock) {
-    if (recv_request.get() != nullptr) {
-      return Failure("Socket is already receiving");
-    }
-    std::swap(request, recv_request);
-  }
-
-  // Extend the life-time of 'this' through the execution of the
-  // lambda in the event loop. Note: The 'self' needs to be explicitly
-  // captured because we're not using it in the body of the lambda. We
-  // can use a 'shared_ptr' because run_in_event_loop is guaranteed to
-  // execute.
-  auto self = shared(this);
-
-  run_in_event_loop(
-      [self]() {
-        CHECK(__in_event_loop__);
-        CHECK(self);
-
-        bool recv = false;
-
-        // We check to see if 'recv_request' is null. It would be null
-        // if a 'discard' happened before this lambda was executed.
-        synchronized (self->lock) {
-          recv = self->recv_request.get() != nullptr;
-        }
-
-        // Only try to read existing data from the bufferevent if the
-        // request has not already been discarded.
-        if (recv) {
-          synchronized (self->bev) {
-            evbuffer* input = bufferevent_get_input(self->bev);
-            size_t length = evbuffer_get_length(input);
-
-            // If there is already data in the buffer or an EOF has
-            // been received, fulfill the 'recv_request' by calling
-            // 'recv_callback()'. Otherwise do nothing and wait for
-            // the 'recv_callback' to run when we receive data over
-            // the network.
-            if (length > 0 || self->received_eof) {
-              self->recv_callback();
-            }
-          }
-        }
-      },
-      DISALLOW_SHORT_CIRCUIT);
-
-  return future;
-}
-
-
-Future<size_t> LibeventSSLSocketImpl::send(const char* data, size_t size)
-{
-  // Optimistically construct a 'SendRequest' and future.
-  Owned<SendRequest> request(new SendRequest(size));
-  Future<size_t> future = request->promise.future();
-
-  // We don't add an 'onDiscard' continuation to send because we can
-  // not accurately detect how many bytes have been sent. Once we pass
-  // the data to the bufferevent, there is the possibility that parts
-  // of it have been sent. Another reason is that if we send partial
-  // messages (discard only a part of the data), then it is likely
-  // that the receiving end will fail parsing the message.
-
-  // Assign 'send_request' under lock, fail on error.
-  synchronized (lock) {
-    if (send_request.get() != nullptr) {
-      return Failure("Socket is already sending");
-    }
-    std::swap(request, send_request);
-  }
-
-  evbuffer* buffer = CHECK_NOTNULL(evbuffer_new());
-
-  int result = evbuffer_add(buffer, data, size);
-  CHECK_EQ(0, result);
-
-  // Extend the life-time of 'this' through the execution of the
-  // lambda in the event loop. Note: The 'self' needs to be explicitly
-  // captured because we're not using it in the body of the lambda. We
-  // can use a 'shared_ptr' because run_in_event_loop is guaranteed to
-  // execute.
-  auto self = shared(this);
-
-  run_in_event_loop(
-      [self, buffer]() {
-        CHECK(__in_event_loop__);
-        CHECK(self);
-
-        // Check if the socket is closed or the write end has
-        // encountered an error in the interim (i.e. we received
-        // a BEV_EVENT_ERROR with BEV_EVENT_WRITING).
-        bool write = false;
-
-        synchronized (self->lock) {
-          if (self->send_request.get() != nullptr) {
-            write = true;
-          }
-        }
-
-        if (write) {
-          int result = bufferevent_write_buffer(self->bev, buffer);
-          CHECK_EQ(0, result);
-        }
-
-        evbuffer_free(buffer);
-      },
-      DISALLOW_SHORT_CIRCUIT);
-
-  return future;
-}
-
-
-Future<size_t> LibeventSSLSocketImpl::sendfile(
-    int_fd fd,
-    off_t offset,
-    size_t size)
-{
-  // Optimistically construct a 'SendRequest' and future.
-  Owned<SendRequest> request(new SendRequest(size));
-  Future<size_t> future = request->promise.future();
-
-  // Assign 'send_request' under lock, fail on error.
-  synchronized (lock) {
-    if (send_request.get() != nullptr) {
-      return Failure("Socket is already sending");
-    }
-    std::swap(request, send_request);
-  }
-
-  // Duplicate the file descriptor because Libevent will take ownership
-  // and control the lifecycle separately.
-  //
-  // TODO(josephw): We can avoid duplicating the file descriptor in
-  // future versions of Libevent. In Libevent versions 2.1.2 and later,
-  // we may use `evbuffer_file_segment_new` and `evbuffer_add_file_segment`
-  // instead of `evbuffer_add_file`.
-  Try<int_fd> dup = os::dup(fd);
-  if (dup.isError()) {
-    return Failure(dup.error());
-  }
-
-  // NOTE: This is *not* an `int_fd` because `libevent` requires a CRT
-  // integer file descriptor, which we allocate and then use
-  // exclusively here.
-#ifdef __WINDOWS__
-  int owned_fd = dup->crt();
-  // The `os::cloexec` and `os::nonblock` functions do nothing on
-  // Windows, and cannot be called because they take `int_fd`.
-#else
-  int owned_fd = dup.get();
-
-  // Set the close-on-exec flag.
-  Try<Nothing> cloexec = os::cloexec(owned_fd);
-  if (cloexec.isError()) {
-    os::close(owned_fd);
-    return Failure(
-        "Failed to set close-on-exec on duplicated file descriptor: " +
-        cloexec.error());
-  }
-
-  // Make the file descriptor non-blocking.
-  Try<Nothing> nonblock = os::nonblock(owned_fd);
-  if (nonblock.isError()) {
-    os::close(owned_fd);
-    return Failure(
-        "Failed to make duplicated file descriptor non-blocking: " +
-        nonblock.error());
-  }
-#endif // __WINDOWS__
-
-  // Extend the life-time of 'this' through the execution of the
-  // lambda in the event loop. Note: The 'self' needs to be explicitly
-  // captured because we're not using it in the body of the lambda. We
-  // can use a 'shared_ptr' because run_in_event_loop is guaranteed to
-  // execute.
-  auto self = shared(this);
-
-  run_in_event_loop(
-      [self, owned_fd, offset, size]() {
-        CHECK(__in_event_loop__);
-        CHECK(self);
-
-        // Check if the socket is closed or the write end has
-        // encountered an error in the interim (i.e. we received
-        // a BEV_EVENT_ERROR with BEV_EVENT_WRITING).
-        bool write = false;
-
-        synchronized (self->lock) {
-          if (self->send_request.get() != nullptr) {
-            write = true;
-          }
-        }
-
-        if (write) {
-          // NOTE: `evbuffer_add_file` will take ownership of the file
-          // descriptor and close it after it has finished reading it.
-          int result = evbuffer_add_file(
-              bufferevent_get_output(self->bev),
-              owned_fd,
-              offset,
-              size);
-          CHECK_EQ(0, result);
-        } else {
-#ifdef __WINDOWS__
-          // NOTE: `os::close()` on Windows is not compatible with CRT
-          // file descriptors, only `HANDLE` and `SOCKET` types.
-          ::_close(owned_fd);
-#else
-          os::close(owned_fd);
-#endif // __WINDOWS__
-        }
-      },
-      DISALLOW_SHORT_CIRCUIT);
-
-  return future;
-}
-
-
-Try<Nothing> LibeventSSLSocketImpl::listen(int backlog)
-{
-  if (listener != nullptr) {
-    return Error("Socket is already listening");
-  }
-
-  CHECK(bev == nullptr);
-
-  // NOTE: Accepted sockets are nonblocking by default in libevent, but
-  // can be set to block via the `LEV_OPT_LEAVE_SOCKETS_BLOCKING`
-  // flag for `evconnlistener_new`.
-  listener = evconnlistener_new(
-      base,
-      [](evconnlistener* listener,
-         evutil_socket_t socket,
-         sockaddr* addr,
-         int addr_length,
-         void* arg) {
-        CHECK(__in_event_loop__);
-
-        std::weak_ptr<LibeventSSLSocketImpl>* handle =
-          reinterpret_cast<std::weak_ptr<LibeventSSLSocketImpl>*>(
-              CHECK_NOTNULL(arg));
-
-        std::shared_ptr<LibeventSSLSocketImpl> impl(handle->lock());
-
-#ifndef __WINDOWS__
-        // NOTE: Passing the flag `LEV_OPT_CLOSE_ON_EXEC` into
-        // `evconnlistener_new` would atomically set `SOCK_CLOEXEC`
-        // on the accepted socket. However, this flag is not supported
-        // in the minimum recommended version of libevent (2.0.22).
-        Try<Nothing> cloexec = os::cloexec(socket);
-        if (cloexec.isError()) {
-          VLOG(2) << "Failed to accept, cloexec: " << cloexec.error();
-
-          // Propagate the error through the listener's `accept_queue`.
-          if (impl != nullptr) {
-            impl->accept_queue.put(
-                Failure("Failed to accept, cloexec: " + cloexec.error()));
-          }
-
-          os::close(socket);
-          return;
-        }
-#endif // __WINDOWS__
-
-        if (impl != nullptr) {
-          Try<net::IP> ip = net::IP::create(*addr);
-          if (ip.isError()) {
-            VLOG(2) << "Could not convert sockaddr to net::IP: " << ip.error();
-          }
-
-          // We pass the 'listener' into the 'AcceptRequest' because
-          // this function could be executed before 'this->listener'
-          // is set.
-          AcceptRequest* request =
-            new AcceptRequest(
-                  // NOTE: The `int_fd` must be explicitly constructed
-                  // to avoid the `intptr_t` being casted to an `int`,
-                  // resulting in a `HANDLE` instead of a `SOCKET` on
-                  // Windows.
-                  int_fd(socket),
-                  listener,
-                  ip.isSome() ? Option<net::IP>(ip.get()) : None());
-
-          impl->accept_callback(request);
-        }
-      },
-      event_loop_handle,
-      LEV_OPT_REUSEABLE,
-      backlog,
-      s);
-
-  if (listener == nullptr) {
-    return Error("Failed to listen on socket");
-  }
-
-  // TODO(jmlvanre): attach an error callback.
-
-  return Nothing();
-}
-
-
-Future<std::shared_ptr<SocketImpl>> LibeventSSLSocketImpl::accept()
-{
-  // Note that due to MESOS-8448, when the caller discards, it's
-  // possible that we pull an accepted socket out of the queue but
-  // drop it when `.then` transitions to discarded rather than
-  // executing the continuation. This is currently acceptable since
-  // callers only discard when they're breaking their accept loop.
-  // However, from an API perspective, we shouldn't be dropping
-  // the socket on the floor.
-  //
-  // We explicitly specify the return type to avoid a type deduction
-  // issue in some versions of clang. See MESOS-2943.
-  return accept_queue.get()
-    .then([](const Future<std::shared_ptr<SocketImpl>>& impl)
-      -> Future<std::shared_ptr<SocketImpl>> {
-      CHECK(!impl.isPending());
-      return impl;
-    });
-}
-
-
-void LibeventSSLSocketImpl::peek_callback(
-    evutil_socket_t fd,
-    short what,
-    void* arg)
-{
-  CHECK(__in_event_loop__);
-
-  CHECK(what & EV_READ);
-  char data[6];
-
-  // Try to peek the first 6 bytes of the message.
-  ssize_t size = ::recv(fd, data, 6, MSG_PEEK);
-
-  // Based on the function 'ssl23_get_client_hello' in openssl, we
-  // test whether to dispatch to the SSL or non-SSL based accept based
-  // on the following rules:
-  //   1. If there are fewer than 3 bytes: non-SSL.
-  //   2. If the 1st bit of the 1st byte is set AND the 3rd byte is
-  //          equal to SSL2_MT_CLIENT_HELLO: SSL.
-  //   3. If the 1st byte is equal to SSL3_RT_HANDSHAKE AND the 2nd
-  //      byte is equal to SSL3_VERSION_MAJOR and the 6th byte is
-  //      equal to SSL3_MT_CLIENT_HELLO: SSL.
-  //   4. Otherwise: non-SSL.
-
-  // For an ascii based protocol to falsely get dispatched to SSL it
-  // needs to:
-  //   1. Start with an invalid ascii character (0x80).
-  //   2. OR have the first 2 characters be a SYN followed by ETX, and
-  //          then the 6th character be SOH.
-  // These conditions clearly do not constitute valid HTTP requests,
-  // and are unlikely to collide with other existing protocols.
-
-  bool ssl = false; // Default to rule 4.
-
-  if (size < 2) { // Rule 1.
-    ssl = false;
-  } else if ((data[0] & 0x80) && data[2] == SSL2_MT_CLIENT_HELLO) { // Rule 2.
-    ssl = true;
-  } else if (data[0] == SSL3_RT_HANDSHAKE &&
-             data[1] == SSL3_VERSION_MAJOR &&
-             data[5] == SSL3_MT_CLIENT_HELLO) { // Rule 3.
-    ssl = true;
-  }
-
-  AcceptRequest* request = reinterpret_cast<AcceptRequest*>(arg);
-
-  // We call 'event_free()' here because it ensures the event is made
-  // non-pending and inactive before it gets deallocated.
-  event_free(request->peek_event);
-  request->peek_event = nullptr;
-
-  if (ssl) {
-    accept_SSL_callback(request);
-  } else {
-    // Downgrade to a non-SSL socket implementation.
-    //
-    // NOTE: The `int_fd` must be explicitly constructed to avoid the
-    // `intptr_t` being casted to an `int`, resulting in a `HANDLE`
-    // instead of a `SOCKET` on Windows.
-    Try<std::shared_ptr<SocketImpl>> impl = PollSocketImpl::create(int_fd(fd));
-    if (impl.isError()) {
-      request->promise.fail(impl.error());
-    } else {
-      request->promise.set(impl.get());
-    }
-
-    delete request;
-  }
-}
-
-
-void LibeventSSLSocketImpl::accept_callback(AcceptRequest* request)
-{
-  CHECK(__in_event_loop__);
-
-  Queue<Future<std::shared_ptr<SocketImpl>>> accept_queue_ = accept_queue;
-
-  // After the socket is accepted, it must complete the SSL
-  // handshake (or be downgraded to a regular socket) before
-  // we put it in the queue of connected sockets.
-  request->promise.future()
-    .onAny([accept_queue_](Future<std::shared_ptr<SocketImpl>> impl) mutable {
-      accept_queue_.put(impl);
-    });
-
-  // If we support downgrading the connection, first wait for this
-  // socket to become readable. We will then MSG_PEEK it to test
-  // whether we want to dispatch as SSL or non-SSL.
-  if (openssl::flags().support_downgrade) {
-    request->peek_event = event_new(
-        base,
-        request->socket,
-        EV_READ,
-        &LibeventSSLSocketImpl::peek_callback,
-        request);
-    event_add(request->peek_event, nullptr);
-  } else {
-    accept_SSL_callback(request);
-  }
-}
-
-
-void LibeventSSLSocketImpl::accept_SSL_callback(AcceptRequest* request)
-{
-  CHECK(__in_event_loop__);
-
-  // Set up SSL object.
-  SSL* ssl = SSL_new(openssl::context());
-  if (ssl == nullptr) {
-    request->promise.fail("Accept failed, SSL_new");
-    delete request;
-    return;
-  }
-
-  // We use 'request->listener' because 'this->listener' may not have
-  // been set by the time this function is executed. See comment in
-  // the lambda for evconnlistener_new in
-  // 'LibeventSSLSocketImpl::listen'.
-  event_base* ev_base = evconnlistener_get_base(request->listener);
-
-  // Construct the bufferevent in the accepting state.
-  bufferevent* bev = bufferevent_openssl_socket_new(
-      ev_base,
-      request->socket,
-      ssl,
-      BUFFEREVENT_SSL_ACCEPTING,
-      BEV_OPT_THREADSAFE);
-
-  if (bev == nullptr) {
-    request->promise.fail("Accept failed: bufferevent_openssl_socket_new");
-    SSL_free(ssl);
-    delete request;
-    return;
-  }
-
-  bufferevent_setcb(
-      bev,
-      nullptr,
-      nullptr,
-      [](bufferevent* bev, short events, void* arg) {
-        // This handles error states or 'BEV_EVENT_CONNECTED' events
-        // and satisfies the promise by constructing a new socket if
-        // the connection was successfuly established.
-        CHECK(__in_event_loop__);
-
-        AcceptRequest* request =
-          reinterpret_cast<AcceptRequest*>(CHECK_NOTNULL(arg));
-
-        if (events & BEV_EVENT_EOF) {
-          request->promise.fail("Failed accept: connection closed");
-        } else if (events & BEV_EVENT_CONNECTED) {
-          // We will receive a 'CONNECTED' state on an accepting socket
-          // once the connection is established. Time to do
-          // post-verification. First, we need to determine the peer
-          // hostname.
-          Option<string> peer_hostname = None();
-
-          if (request->ip.isSome()) {
-            Try<string> hostname = net::getHostname(request->ip.get());
-
-            if (hostname.isError()) {
-              VLOG(2) << "Could not determine hostname of peer: "
-                      << hostname.error();
-            } else {
-              VLOG(2) << "Accepting from " << hostname.get();
-              peer_hostname = hostname.get();
-            }
-          }
-
-          SSL* ssl = bufferevent_openssl_get_ssl(bev);
-          CHECK_NOTNULL(ssl);
-
-          Try<Nothing> verify =
-            openssl::verify(ssl, peer_hostname, request->ip);
-
-          if (verify.isError()) {
-            VLOG(1) << "Failed accept, verification error: " << verify.error();
-            request->promise.fail(verify.error());
-            SSL_free(ssl);
-            bufferevent_free(bev);
-            // TODO(jmlvanre): Clean up for readability. Consider RAII
-            // or constructing the impl earlier.
-            CHECK(request->socket >= 0);
-            Try<Nothing> close = os::close(request->socket);
-            if (close.isError()) {
-              LOG(FATAL)
-                << "Failed to close socket " << stringify(request->socket)
-                << ": " << close.error();
-            }
-            delete request;
-            return;
-          }
-
-          auto impl = std::shared_ptr<LibeventSSLSocketImpl>(
-              new LibeventSSLSocketImpl(
-                  request->socket,
-                  bev,
-                  std::move(peer_hostname)));
-
-          // See comment at 'initialize' declaration for why we call
-          // this.
-          impl->initialize();
-
-          // We have to wait till after 'initialize()' is invoked for
-          // event_loop_handle to be valid as a callback argument for
-          // the callbacks.
-          bufferevent_setcb(
-              CHECK_NOTNULL(impl->bev),
-              &LibeventSSLSocketImpl::recv_callback,
-              &LibeventSSLSocketImpl::send_callback,
-              &LibeventSSLSocketImpl::event_callback,
-              CHECK_NOTNULL(impl->event_loop_handle));
-
-          request->promise.set(std::dynamic_pointer_cast<SocketImpl>(impl));
-        } else if (events & BEV_EVENT_ERROR) {
-          std::ostringstream stream;
-          if (EVUTIL_SOCKET_ERROR() != 0) {
-            stream << evutil_socket_error_to_string(EVUTIL_SOCKET_ERROR());
-          } else {
-            char buffer[1024] = {};
-            unsigned long error = bufferevent_get_openssl_error(bev);
-            ERR_error_string_n(error, buffer, sizeof(buffer));
-            stream << buffer;
-          }
-
-          // Fail the accept request and log the error.
-          VLOG(1) << "Socket error: " << stream.str();
-
-          SSL* ssl = bufferevent_openssl_get_ssl(CHECK_NOTNULL(bev));
-          SSL_free(ssl);
-          bufferevent_free(bev);
-
-          // TODO(jmlvanre): Clean up for readability. Consider RAII
-          // or constructing the impl earlier.
-          CHECK(request->socket >= 0);
-          Try<Nothing> close = os::close(request->socket);
-          if (close.isError()) {
-            LOG(FATAL)
-              << "Failed to close socket " << stringify(request->socket)
-              << ": " << close.error();
-          }
-          request->promise.fail(
-              "Failed accept: connection error: " + stream.str());
-        }
-
-        delete request;
-      },
-      request);
-}
-
-} // namespace internal {
-} // namespace network {
-} // namespace process {

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/libevent_ssl_socket.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/libevent_ssl_socket.hpp b/3rdparty/libprocess/src/libevent_ssl_socket.hpp
deleted file mode 100644
index 6ef5a86..0000000
--- a/3rdparty/libprocess/src/libevent_ssl_socket.hpp
+++ /dev/null
@@ -1,198 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License
-
-#ifndef __LIBEVENT_SSL_SOCKET_HPP__
-#define __LIBEVENT_SSL_SOCKET_HPP__
-
-#include <event2/buffer.h>
-#include <event2/bufferevent_ssl.h>
-#include <event2/event.h>
-#include <event2/listener.h>
-#include <event2/util.h>
-
-#include <atomic>
-#include <memory>
-
-#include <process/queue.hpp>
-#include <process/socket.hpp>
-
-namespace process {
-namespace network {
-namespace internal {
-
-class LibeventSSLSocketImpl : public SocketImpl
-{
-public:
-  // See 'Socket::create()'.
-  static Try<std::shared_ptr<SocketImpl>> create(int_fd s);
-
-  LibeventSSLSocketImpl(int_fd _s);
-
-  ~LibeventSSLSocketImpl() override;
-
-  // Implement 'SocketImpl' interface.
-  Future<Nothing> connect(const Address& address) override;
-  Future<size_t> recv(char* data, size_t size) override;
-  // Send does not currently support discard. See implementation.
-  Future<size_t> send(const char* data, size_t size) override;
-  Future<size_t> sendfile(int_fd fd, off_t offset, size_t size) override;
-  Try<Nothing> listen(int backlog) override;
-  Future<std::shared_ptr<SocketImpl>> accept() override;
-  SocketImpl::Kind kind() const override { return SocketImpl::Kind::SSL; }
-
-  // Shuts down the socket.
-  //
-  // NOTE: Although this method accepts an integer which specifies the
-  // shutdown mode, this parameter is ignored because SSL connections
-  // do not have a concept of read/write-only shutdown. If either end
-  // of the socket is closed, then the futures of any outstanding read
-  // requests will be completed (possibly as failures).
-  Try<Nothing, SocketError> shutdown(int how) override;
-
-  // We need a post-initializer because 'shared_from_this()' is not
-  // valid until the constructor has finished.
-  void initialize();
-
-private:
-  // A set of helper functions that transitions an accepted socket to
-  // an SSL connected socket. With the libevent-openssl library, once
-  // we return from the 'accept_callback()' which is scheduled by
-  // 'listen' then we still need to wait for the 'BEV_EVENT_CONNECTED'
-  // state before we know the SSL connection has been established.
-  struct AcceptRequest
-  {
-    AcceptRequest(
-        int_fd _socket,
-        evconnlistener* _listener,
-        const Option<net::IP>& _ip)
-      : peek_event(nullptr),
-        listener(_listener),
-        socket(_socket),
-        ip(_ip) {}
-    event* peek_event;
-    Promise<std::shared_ptr<SocketImpl>> promise;
-    evconnlistener* listener;
-    int_fd socket;
-    Option<net::IP> ip;
-  };
-
-  struct RecvRequest
-  {
-    RecvRequest(char* _data, size_t _size)
-      : data(_data), size(_size) {}
-    Promise<size_t> promise;
-    char* data;
-    size_t size;
-  };
-
-  struct SendRequest
-  {
-    SendRequest(size_t _size)
-      : size(_size) {}
-    Promise<size_t> promise;
-    size_t size;
-  };
-
-  struct ConnectRequest
-  {
-    Promise<Nothing> promise;
-  };
-
-  // This is a private constructor used by the accept helper
-  // functions.
-  LibeventSSLSocketImpl(
-      int_fd _s,
-      bufferevent* bev,
-      Option<std::string>&& peer_hostname);
-
-  // This is called when the equivalent of 'accept' returns. The role
-  // of this function is to set up the SSL object and bev. If we
-  // support both SSL and non-SSL traffic simultaneously then we first
-  // wait for data to be ready and test the hello handshake to
-  // disambiguate between the kinds of traffic.
-  void accept_callback(AcceptRequest* request);
-
-  // This is the continuation of 'accept_callback' that handles an SSL
-  // connection.
-  static void accept_SSL_callback(AcceptRequest* request);
-
-  // This function peeks at the data on an accepted socket to see if
-  // there is an SSL handshake or not. It then dispatches to the
-  // SSL handling function or creates a non-SSL socket.
-  static void peek_callback(evutil_socket_t fd, short what, void* arg);
-
-  // The following are function pairs of static functions to member
-  // functions. The static functions test and hold the weak pointer to
-  // the socket before calling the member functions. This protects
-  // against the socket being destroyed before the event loop calls
-  // the callbacks.
-  static void recv_callback(bufferevent* bev, void* arg);
-  void recv_callback();
-
-  static void send_callback(bufferevent* bev, void* arg);
-  void send_callback();
-
-  static void event_callback(bufferevent* bev, short events, void* arg);
-  void event_callback(short events);
-
-  bufferevent* bev;
-
-  evconnlistener* listener;
-
-  // Protects the following instance variables.
-  std::atomic_flag lock = ATOMIC_FLAG_INIT;
-  Owned<RecvRequest> recv_request;
-  Owned<SendRequest> send_request;
-  Owned<ConnectRequest> connect_request;
-
-  // Indicates whether or not an EOF has been received on this socket.
-  // Our accesses to this member are not synchronized because they all
-  // occur within the event loop, which runs on a single thread.
-  bool received_eof = false;
-
-  // This is a weak pointer to 'this', i.e., ourselves, this class
-  // instance. We need this for our event loop callbacks because it's
-  // possible that we'll actually want to cleanup this socket impl
-  // before the event loop callback gets executed ... and we'll check
-  // in each event loop callback whether or not this weak_ptr is valid
-  // by attempting to upgrade it to shared_ptr. It is the
-  // responsibility of the event loop through the deferred lambda in
-  // the destructor to clean up this pointer.
-  // 1) It is a 'weak_ptr' as opposed to a 'shared_ptr' because we
-  // want to test whether the object is still around from within the
-  // event loop. If it was a 'shared_ptr' then we would be
-  // contributing to the lifetime of the object and would no longer be
-  // able to test the lifetime correctly.
-  // 2) This is a pointer to a 'weak_ptr' so that we can pass this
-  // through to the event loop through the C-interface. We need access
-  // to the 'weak_ptr' from outside the object (in the event loop) to
-  // test if the object is still alive. By maintaining this 'weak_ptr'
-  // on the heap we can be sure it is safe to access from the
-  // event loop until it is destroyed.
-  std::weak_ptr<LibeventSSLSocketImpl>* event_loop_handle;
-
-  // This queue stores accepted sockets that are considered connected
-  // (either the SSL handshake has completed or the socket has been
-  // downgraded). The 'accept()' call returns sockets from this queue.
-  // We wrap the socket in a 'Future' so that we can pass failures or
-  // discards through.
-  Queue<Future<std::shared_ptr<SocketImpl>>> accept_queue;
-
-  Option<std::string> peer_hostname;
-  Option<net::IP> peer_ip;
-};
-
-} // namespace internal {
-} // namespace network {
-} // namespace process {
-
-#endif // __LIBEVENT_SSL_SOCKET_HPP__


[07/16] mesos git commit: Changed use of `os::nonblock` to `io::prepare_async`.

Posted by an...@apache.org.
Changed use of `os::nonblock` to `io::prepare_async`.

Due to the previous patch, instances that use `os::nonblock` before
sending the file descriptor to the async IO functions now need to use
`io::prepare_async`.

Review: https://reviews.apache.org/r/67387/


Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/379e56b8
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/379e56b8
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/379e56b8

Branch: refs/heads/master
Commit: 379e56b86c8cd40a04a7e7c55b3b993747f77e1c
Parents: b6f3d71
Author: Akash Gupta <ak...@hotmail.com>
Authored: Wed Jun 27 14:30:13 2018 -0700
Committer: Andrew Schwartzmeyer <an...@schwartzmeyer.com>
Committed: Wed Jun 27 15:06:10 2018 -0700

----------------------------------------------------------------------
 src/files/files.cpp                       | 6 +++---
 src/slave/container_loggers/logrotate.cpp | 6 +++---
 2 files changed, 6 insertions(+), 6 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/379e56b8/src/files/files.cpp
----------------------------------------------------------------------
diff --git a/src/files/files.cpp b/src/files/files.cpp
index 8d896ed..4b8713a 100644
--- a/src/files/files.cpp
+++ b/src/files/files.cpp
@@ -711,10 +711,10 @@ Future<Try<tuple<size_t, string>, FilesError>> FilesProcess::_read(
     return FilesError(FilesError::Type::UNKNOWN, error);
   }
 
-  Try<Nothing> nonblock = os::nonblock(fd.get());
-  if (nonblock.isError()) {
+  Try<Nothing> async = io::prepare_async(fd.get());
+  if (async.isError()) {
     string error =
-        "Failed to set file descriptor nonblocking: " + nonblock.error();
+        "Failed to make file descriptor asynchronous: " + async.error();
     LOG(WARNING) << error;
     os::close(fd.get());
     return FilesError(FilesError::Type::UNKNOWN, error);

http://git-wip-us.apache.org/repos/asf/mesos/blob/379e56b8/src/slave/container_loggers/logrotate.cpp
----------------------------------------------------------------------
diff --git a/src/slave/container_loggers/logrotate.cpp b/src/slave/container_loggers/logrotate.cpp
index bd41912..ff02130 100644
--- a/src/slave/container_loggers/logrotate.cpp
+++ b/src/slave/container_loggers/logrotate.cpp
@@ -99,9 +99,9 @@ public:
     }
 
     // NOTE: This is a prerequisuite for `io::read`.
-    Try<Nothing> nonblock = os::nonblock(STDIN_FILENO);
-    if (nonblock.isError()) {
-      return Failure("Failed to set nonblocking pipe: " + nonblock.error());
+    Try<Nothing> async = io::prepare_async(STDIN_FILENO);
+    if (async.isError()) {
+      return Failure("Failed to set async pipe: " + async.error());
     }
 
     // NOTE: This does not block.


[08/16] mesos git commit: Organized POSIX and Windows libprocess implementations.

Posted by an...@apache.org.
http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/posix/poll_socket.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/posix/poll_socket.cpp b/3rdparty/libprocess/src/posix/poll_socket.cpp
new file mode 100644
index 0000000..74acb69
--- /dev/null
+++ b/3rdparty/libprocess/src/posix/poll_socket.cpp
@@ -0,0 +1,278 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+
+#ifdef __WINDOWS__
+#include <stout/windows.hpp>
+#else
+#include <netinet/tcp.h>
+#endif // __WINDOWS__
+
+#include <process/io.hpp>
+#include <process/loop.hpp>
+#include <process/network.hpp>
+#include <process/socket.hpp>
+
+#include <stout/os/sendfile.hpp>
+#include <stout/os/strerror.hpp>
+#include <stout/os.hpp>
+
+#include "config.hpp"
+#include "poll_socket.hpp"
+
+using std::string;
+
+namespace process {
+namespace network {
+namespace internal {
+
+Try<std::shared_ptr<SocketImpl>> PollSocketImpl::create(int_fd s)
+{
+  return std::make_shared<PollSocketImpl>(s);
+}
+
+
+Try<Nothing> PollSocketImpl::listen(int backlog)
+{
+  if (::listen(get(), backlog) < 0) {
+    return ErrnoError();
+  }
+  return Nothing();
+}
+
+
+Future<std::shared_ptr<SocketImpl>> PollSocketImpl::accept()
+{
+  // Need to hold a copy of `this` so that the underlying socket
+  // doesn't end up getting reused before we return from the call to
+  // `io::poll` and end up accepting a socket incorrectly.
+  auto self = shared(this);
+
+  return io::poll(get(), io::READ)
+    .then([self]() -> Future<std::shared_ptr<SocketImpl>> {
+      Try<int_fd> accepted = network::accept(self->get());
+      if (accepted.isError()) {
+        return Failure(accepted.error());
+      }
+
+      int_fd s = accepted.get();
+      Try<Nothing> nonblock = os::nonblock(s);
+      if (nonblock.isError()) {
+        os::close(s);
+        return Failure("Failed to accept, nonblock: " + nonblock.error());
+      }
+
+      Try<Nothing> cloexec = os::cloexec(s);
+      if (cloexec.isError()) {
+        os::close(s);
+        return Failure("Failed to accept, cloexec: " + cloexec.error());
+      }
+
+      Try<Address> address = network::address(s);
+      if (address.isError()) {
+        os::close(s);
+        return Failure("Failed to get address: " + address.error());
+      }
+
+      // Turn off Nagle (TCP_NODELAY) so pipelined requests don't wait.
+      // NOTE: We cast to `char*` here because the function prototypes
+      // on Windows use `char*` instead of `void*`.
+      if (address->family() == Address::Family::INET4 ||
+          address->family() == Address::Family::INET6) {
+        int on = 1;
+        if (::setsockopt(
+                s,
+                SOL_TCP,
+                TCP_NODELAY,
+                reinterpret_cast<const char*>(&on),
+                sizeof(on)) < 0) {
+          const string error = os::strerror(errno);
+          os::close(s);
+          return Failure(
+              "Failed to turn off the Nagle algorithm: " + stringify(error));
+        }
+      }
+
+      Try<std::shared_ptr<SocketImpl>> impl = create(s);
+      if (impl.isError()) {
+        os::close(s);
+        return Failure("Failed to create socket: " + impl.error());
+      }
+
+      return impl.get();
+    });
+}
+
+
+Future<Nothing> PollSocketImpl::connect(const Address& address)
+{
+  Try<Nothing, SocketError> connect = network::connect(get(), address);
+  if (connect.isError()) {
+    if (net::is_inprogress_error(connect.error().code)) {
+      // Need to hold a copy of `this` so that the underlying socket
+      // doesn't end up getting reused before we return from the call
+      // to `io::poll` and end up connecting incorrectly.
+      auto self = shared(this);
+
+      return io::poll(get(), io::WRITE)
+        .then([self, address]() -> Future<Nothing> {
+          // Now check that a successful connection was made.
+          int opt;
+          socklen_t optlen = sizeof(opt);
+
+          // NOTE: We cast to `char*` here because the function
+          // prototypes on Windows use `char*` instead of `void*`.
+          if (::getsockopt(
+                  self->get(),
+                  SOL_SOCKET,
+                  SO_ERROR,
+                  reinterpret_cast<char*>(&opt),
+                  &optlen) < 0) {
+            return Failure(SocketError(
+                "Failed to get status of connect to " + stringify(address)));
+          }
+
+          if (opt != 0) {
+            return Failure(SocketError(
+                opt,
+                "Failed to connect to " +
+                stringify(address)));
+          }
+
+          return Nothing();
+        });
+    }
+
+    return Failure(connect.error());
+  }
+
+  return Nothing();
+}
+
+
+Future<size_t> PollSocketImpl::recv(char* data, size_t size)
+{
+  // Need to hold a copy of `this` so that the underlying socket
+  // doesn't end up getting reused before we return from the call to
+  // `io::read` and end up reading data incorrectly.
+  auto self = shared(this);
+
+  return io::read(get(), data, size)
+    .then([self](size_t length) {
+      return length;
+    });
+}
+
+
+Future<size_t> PollSocketImpl::send(const char* data, size_t size)
+{
+  CHECK(size > 0); // TODO(benh): Just return 0 if `size` is 0?
+
+  // Need to hold a copy of `this` so that the underlying socket
+  // doesn't end up getting reused before we return.
+  auto self = shared(this);
+
+  // TODO(benh): Reuse `io::write`? Or is `net::send` and
+  // `MSG_NOSIGNAL` critical here?
+  return loop(
+      None(),
+      [self, data, size]() -> Future<Option<size_t>> {
+        while (true) {
+          ssize_t length = net::send(self->get(), data, size, MSG_NOSIGNAL);
+
+          if (length < 0) {
+#ifdef __WINDOWS__
+            int error = WSAGetLastError();
+#else
+            int error = errno;
+#endif // __WINDOWS__
+
+            if (net::is_restartable_error(error)) {
+              // Interrupted, try again now.
+              continue;
+            } else if (!net::is_retryable_error(error)) {
+              // TODO(benh): Confirm that `os::strerror` does the
+              // right thing for `error` on Windows.
+              VLOG(1) << "Socket error while sending: " << os::strerror(error);
+              return Failure(os::strerror(error));
+            }
+
+            return None();
+          }
+
+          return length;
+        }
+      },
+      [self](const Option<size_t>& length) -> Future<ControlFlow<size_t>> {
+        // Retry after we've polled if we don't yet have a result.
+        if (length.isNone()) {
+          return io::poll(self->get(), io::WRITE)
+            .then([](short event) -> ControlFlow<size_t> {
+              CHECK_EQ(io::WRITE, event);
+              return Continue();
+            });
+        }
+        return Break(length.get());
+      });
+}
+
+
+Future<size_t> PollSocketImpl::sendfile(int_fd fd, off_t offset, size_t size)
+{
+  CHECK(size > 0); // TODO(benh): Just return 0 if `size` is 0?
+
+  // Need to hold a copy of `this` so that the underlying socket
+  // doesn't end up getting reused before we return.
+  auto self = shared(this);
+
+  return loop(
+      None(),
+      [self, fd, offset, size]() -> Future<Option<size_t>> {
+        while (true) {
+          Try<ssize_t, SocketError> length = os::sendfile(
+              self->get(),
+              fd,
+              offset,
+              size);
+
+          if (length.isSome()) {
+            CHECK(length.get() >= 0);
+            return length.get();
+          }
+
+          if (net::is_restartable_error(length.error().code)) {
+            // Interrupted, try again now.
+            continue;
+          } else if (!net::is_retryable_error(length.error().code)) {
+            VLOG(1) << length.error().message;
+            return Failure(length.error());
+          }
+
+          return None();
+        }
+      },
+      [self](const Option<size_t>& length) -> Future<ControlFlow<size_t>> {
+        // Retry after we've polled if we don't yet have a result.
+        if (length.isNone()) {
+          return io::poll(self->get(), io::WRITE)
+            .then([](short event) -> ControlFlow<size_t> {
+              CHECK_EQ(io::WRITE, event);
+              return Continue();
+            });
+        }
+        return Break(length.get());
+      });
+}
+
+} // namespace internal {
+} // namespace network {
+} // namespace process {

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/posix/subprocess.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/posix/subprocess.cpp b/3rdparty/libprocess/src/posix/subprocess.cpp
new file mode 100644
index 0000000..01e3272
--- /dev/null
+++ b/3rdparty/libprocess/src/posix/subprocess.cpp
@@ -0,0 +1,102 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <glog/logging.h>
+
+#include <process/future.hpp>
+#include <process/reap.hpp>
+#include <process/subprocess.hpp>
+
+#include <stout/error.hpp>
+#include <stout/lambda.hpp>
+#include <stout/foreach.hpp>
+#include <stout/option.hpp>
+#include <stout/os.hpp>
+#include <stout/os/strerror.hpp>
+#include <stout/strings.hpp>
+#include <stout/try.hpp>
+
+using std::map;
+using std::string;
+using std::vector;
+
+namespace process {
+
+using InputFileDescriptors = Subprocess::IO::InputFileDescriptors;
+using OutputFileDescriptors = Subprocess::IO::OutputFileDescriptors;
+
+
+Subprocess::IO Subprocess::PIPE()
+{
+  return Subprocess::IO(
+      []() -> Try<InputFileDescriptors> {
+        int pipefd[2];
+        if (::pipe(pipefd) == -1) {
+          return ErrnoError("Failed to create pipe");
+        }
+
+        InputFileDescriptors fds;
+        fds.read = pipefd[0];
+        fds.write = pipefd[1];
+        return fds;
+      },
+      []() -> Try<OutputFileDescriptors> {
+        int pipefd[2];
+        if (::pipe(pipefd) == -1) {
+          return ErrnoError("Failed to create pipe");
+        }
+
+        OutputFileDescriptors fds;
+        fds.read = pipefd[0];
+        fds.write = pipefd[1];
+        return fds;
+      });
+}
+
+
+Subprocess::IO Subprocess::PATH(const string& path)
+{
+  return Subprocess::IO(
+      [path]() -> Try<InputFileDescriptors> {
+        Try<int> open = os::open(path, O_RDONLY | O_CLOEXEC);
+        if (open.isError()) {
+          return Error("Failed to open '" + path + "': " + open.error());
+        }
+
+        InputFileDescriptors fds;
+        fds.read = open.get();
+        return fds;
+      },
+      [path]() -> Try<OutputFileDescriptors> {
+        Try<int> open = os::open(
+            path,
+            O_WRONLY | O_CREAT | O_APPEND | O_CLOEXEC,
+            S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+        if (open.isError()) {
+          return Error("Failed to open '" + path + "': " + open.error());
+        }
+
+        OutputFileDescriptors fds;
+        fds.write = open.get();
+        return fds;
+      });
+}
+
+}  // namespace process {

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/posix/subprocess.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/posix/subprocess.hpp b/3rdparty/libprocess/src/posix/subprocess.hpp
new file mode 100644
index 0000000..007058b
--- /dev/null
+++ b/3rdparty/libprocess/src/posix/subprocess.hpp
@@ -0,0 +1,355 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#ifndef __PROCESS_POSIX_SUBPROCESS_HPP__
+#define __PROCESS_POSIX_SUBPROCESS_HPP__
+
+#ifdef __linux__
+#include <sys/prctl.h>
+#endif // __linux__
+#include <sys/types.h>
+
+#include <string>
+
+#include <glog/logging.h>
+
+#include <process/subprocess.hpp>
+
+#include <stout/check.hpp>
+#include <stout/error.hpp>
+#include <stout/exit.hpp>
+#include <stout/foreach.hpp>
+#include <stout/hashset.hpp>
+#include <stout/nothing.hpp>
+#include <stout/lambda.hpp>
+#include <stout/none.hpp>
+#include <stout/option.hpp>
+#include <stout/os.hpp>
+#include <stout/try.hpp>
+#include <stout/unreachable.hpp>
+
+#include <stout/os/close.hpp>
+#include <stout/os/environment.hpp>
+#include <stout/os/fcntl.hpp>
+#include <stout/os/signals.hpp>
+#include <stout/os/strerror.hpp>
+
+namespace process {
+namespace internal {
+
+static void close(std::initializer_list<int_fd> fds);
+
+
+static void close(
+    const Subprocess::IO::InputFileDescriptors& stdinfds,
+    const Subprocess::IO::OutputFileDescriptors& stdoutfds,
+    const Subprocess::IO::OutputFileDescriptors& stderrfds);
+
+
+inline pid_t defaultClone(const lambda::function<int()>& func)
+{
+  pid_t pid = ::fork();
+  if (pid == -1) {
+    return -1;
+  } else if (pid == 0) {
+    // Child.
+    ::exit(func());
+    UNREACHABLE();
+  } else {
+    // Parent.
+    return pid;
+  }
+}
+
+
+// This function will invoke `os::cloexec` on all specified file
+// descriptors that are valid (i.e., not `None` and >= 0).
+inline Try<Nothing> cloexec(
+    const InputFileDescriptors& stdinfds,
+    const OutputFileDescriptors& stdoutfds,
+    const OutputFileDescriptors& stderrfds)
+{
+  hashset<int> fds = {
+    stdinfds.read,
+    stdinfds.write.getOrElse(-1),
+    stdoutfds.read.getOrElse(-1),
+    stdoutfds.write,
+    stderrfds.read.getOrElse(-1),
+    stderrfds.write
+  };
+
+  foreach (int fd, fds) {
+    if (fd >= 0) {
+      Try<Nothing> cloexec = os::cloexec(fd);
+      if (cloexec.isError()) {
+        return Error(cloexec.error());
+      }
+    }
+  }
+
+  return Nothing();
+}
+
+
+// The main entry of the child process.
+//
+// NOTE: This function has to be async signal safe.
+inline int childMain(
+    const std::string& path,
+    char** argv,
+    char** envp,
+    const InputFileDescriptors& stdinfds,
+    const OutputFileDescriptors& stdoutfds,
+    const OutputFileDescriptors& stderrfds,
+    bool blocking,
+    int pipes[2],
+    const std::vector<Subprocess::ChildHook>& child_hooks)
+{
+  // Close parent's end of the pipes.
+  if (stdinfds.write.isSome()) {
+    ::close(stdinfds.write.get());
+  }
+  if (stdoutfds.read.isSome()) {
+    ::close(stdoutfds.read.get());
+  }
+  if (stderrfds.read.isSome()) {
+    ::close(stderrfds.read.get());
+  }
+
+  // Currently we will block the child's execution of the new process
+  // until all the parent hooks (if any) have executed.
+  if (blocking) {
+    ::close(pipes[1]);
+  }
+
+  // Redirect I/O for stdin/stdout/stderr.
+  while (::dup2(stdinfds.read, STDIN_FILENO) == -1 && errno == EINTR);
+  while (::dup2(stdoutfds.write, STDOUT_FILENO) == -1 && errno == EINTR);
+  while (::dup2(stderrfds.write, STDERR_FILENO) == -1 && errno == EINTR);
+
+  // Close the copies. We need to make sure that we do not close the
+  // file descriptor assigned to stdin/stdout/stderr in case the
+  // parent has closed stdin/stdout/stderr when calling this
+  // function (in that case, a dup'ed file descriptor may have the
+  // same file descriptor number as stdin/stdout/stderr).
+  //
+  // We also need to ensure that we don't "double close" any file
+  // descriptors in the case where one of stdinfds.read,
+  // stdoutfds.write, or stdoutfds.write are equal.
+  if (stdinfds.read != STDIN_FILENO &&
+      stdinfds.read != STDOUT_FILENO &&
+      stdinfds.read != STDERR_FILENO) {
+    ::close(stdinfds.read);
+  }
+  if (stdoutfds.write != STDIN_FILENO &&
+      stdoutfds.write != STDOUT_FILENO &&
+      stdoutfds.write != STDERR_FILENO &&
+      stdoutfds.write != stdinfds.read) {
+    ::close(stdoutfds.write);
+  }
+  if (stderrfds.write != STDIN_FILENO &&
+      stderrfds.write != STDOUT_FILENO &&
+      stderrfds.write != STDERR_FILENO &&
+      stderrfds.write != stdinfds.read &&
+      stderrfds.write != stdoutfds.write) {
+    ::close(stderrfds.write);
+  }
+
+  if (blocking) {
+    // Do a blocking read on the pipe until the parent signals us to
+    // continue.
+    char dummy;
+    ssize_t length;
+    while ((length = ::read(pipes[0], &dummy, sizeof(dummy))) == -1 &&
+          errno == EINTR);
+
+    if (length != sizeof(dummy)) {
+      ABORT("Failed to synchronize with parent");
+    }
+
+    // Now close the pipe as we don't need it anymore.
+    ::close(pipes[0]);
+  }
+
+  // Run the child hooks.
+  foreach (const Subprocess::ChildHook& hook, child_hooks) {
+    Try<Nothing> callback = hook();
+
+    // If the callback failed, we should abort execution.
+    if (callback.isError()) {
+      ABORT("Failed to execute Subprocess::ChildHook: " + callback.error());
+    }
+  }
+
+  os::execvpe(path.c_str(), argv, envp);
+
+  SAFE_EXIT(
+      errno, "Failed to os::execvpe on path '%s': %d", path.c_str(), errno);
+}
+
+
+inline Try<pid_t> cloneChild(
+    const std::string& path,
+    std::vector<std::string> argv,
+    const Option<std::map<std::string, std::string>>& environment,
+    const Option<lambda::function<
+        pid_t(const lambda::function<int()>&)>>& _clone,
+    const std::vector<Subprocess::ParentHook>& parent_hooks,
+    const std::vector<Subprocess::ChildHook>& child_hooks,
+    const InputFileDescriptors stdinfds,
+    const OutputFileDescriptors stdoutfds,
+    const OutputFileDescriptors stderrfds)
+{
+  // The real arguments that will be passed to 'os::execvpe'. We need
+  // to construct them here before doing the clone as it might not be
+  // async signal safe to perform the memory allocation.
+  char** _argv = new char*[argv.size() + 1];
+  for (size_t i = 0; i < argv.size(); i++) {
+    _argv[i] = (char*) argv[i].c_str();
+  }
+  _argv[argv.size()] = nullptr;
+
+  // Like above, we need to construct the environment that we'll pass
+  // to 'os::execvpe' as it might not be async-safe to perform the
+  // memory allocations.
+  char** envp = os::raw::environment();
+
+  if (environment.isSome()) {
+    // NOTE: We add 1 to the size for a `nullptr` terminator.
+    envp = new char*[environment->size() + 1];
+
+    size_t index = 0;
+    foreachpair (
+        const std::string& key,
+        const std::string& value, environment.get()) {
+      std::string entry = key + "=" + value;
+      envp[index] = new char[entry.size() + 1];
+      strncpy(envp[index], entry.c_str(), entry.size() + 1);
+      ++index;
+    }
+
+    envp[index] = nullptr;
+  }
+
+  // Determine the function to clone the child process. If the user
+  // does not specify the clone function, we will use the default.
+  lambda::function<pid_t(const lambda::function<int()>&)> clone =
+    (_clone.isSome() ? _clone.get() : defaultClone);
+
+  // Currently we will block the child's execution of the new process
+  // until all the `parent_hooks` (if any) have executed.
+  std::array<int, 2> pipes;
+  const bool blocking = !parent_hooks.empty();
+
+  if (blocking) {
+    // We assume this should not fail under reasonable conditions so we
+    // use CHECK.
+    Try<std::array<int, 2>> pipe = os::pipe();
+    CHECK_SOME(pipe);
+
+    pipes = pipe.get();
+  }
+
+  // Now, clone the child process.
+  pid_t pid = clone(lambda::bind(
+      &childMain,
+      path,
+      _argv,
+      envp,
+      stdinfds,
+      stdoutfds,
+      stderrfds,
+      blocking,
+      pipes.data(),
+      child_hooks));
+
+  delete[] _argv;
+
+  // Need to delete 'envp' if we had environment variables passed to
+  // us and we needed to allocate the space.
+  if (environment.isSome()) {
+    CHECK_NE(os::raw::environment(), envp);
+
+    // We ignore the last 'envp' entry since it is nullptr.
+    for (size_t index = 0; index < environment->size(); index++) {
+      delete[] envp[index];
+    }
+
+    delete[] envp;
+  }
+
+  if (pid == -1) {
+    // Save the errno as 'close' below might overwrite it.
+    ErrnoError error("Failed to clone");
+    internal::close(stdinfds, stdoutfds, stderrfds);
+
+    if (blocking) {
+      os::close(pipes[0]);
+      os::close(pipes[1]);
+    }
+
+    return error;
+  }
+
+  // Close the child-ends of the file descriptors that are created by
+  // this function.
+  internal::close({stdinfds.read, stdoutfds.write, stderrfds.write});
+
+  if (blocking) {
+    os::close(pipes[0]);
+
+    // Run the parent hooks.
+    foreach (const Subprocess::ParentHook& hook, parent_hooks) {
+      Try<Nothing> parentSetup = hook.parent_setup(pid);
+
+      // If the hook callback fails, we shouldn't proceed with the
+      // execution and hence the child process should be killed.
+      if (parentSetup.isError()) {
+        LOG(WARNING)
+          << "Failed to execute Subprocess::ParentHook in parent for child '"
+          << pid << "': " << parentSetup.error();
+
+        os::close(pipes[1]);
+
+        // Ensure the child is killed.
+        ::kill(pid, SIGKILL);
+
+        return Error(
+            "Failed to execute Subprocess::ParentHook in parent for child '" +
+            stringify(pid) + "': " + parentSetup.error());
+      }
+    }
+
+    // Now that we've executed the parent hooks, we can signal the child to
+    // continue by writing to the pipe.
+    char dummy;
+    ssize_t length;
+    while ((length = ::write(pipes[1], &dummy, sizeof(dummy))) == -1 &&
+           errno == EINTR);
+
+    os::close(pipes[1]);
+
+    if (length != sizeof(dummy)) {
+      // Ensure the child is killed.
+      ::kill(pid, SIGKILL);
+
+      return Error("Failed to synchronize child process");
+    }
+  }
+
+  return pid;
+}
+
+}  // namespace internal {
+}  // namespace process {
+
+#endif // __PROCESS_POSIX_SUBPROCESS_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/socket.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/socket.cpp b/3rdparty/libprocess/src/socket.cpp
index 3031cd8..606a1c4 100644
--- a/3rdparty/libprocess/src/socket.cpp
+++ b/3rdparty/libprocess/src/socket.cpp
@@ -27,7 +27,7 @@
 #include <stout/unreachable.hpp>
 
 #ifdef USE_SSL_SOCKET
-#include "libevent_ssl_socket.hpp"
+#include "posix/libevent/libevent_ssl_socket.hpp"
 #endif
 #include "poll_socket.hpp"
 

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/subprocess.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/subprocess.cpp b/3rdparty/libprocess/src/subprocess.cpp
index 0b2c02a..c0640de 100644
--- a/3rdparty/libprocess/src/subprocess.cpp
+++ b/3rdparty/libprocess/src/subprocess.cpp
@@ -47,9 +47,9 @@
 #include <stout/os/signals.hpp>
 
 #ifdef __WINDOWS__
-#include "subprocess_windows.hpp"
+#include "windows/subprocess.hpp"
 #else
-#include "subprocess_posix.hpp"
+#include "posix/subprocess.hpp"
 #endif // __WINDOWS__
 
 using std::map;

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/subprocess_posix.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/subprocess_posix.cpp b/3rdparty/libprocess/src/subprocess_posix.cpp
deleted file mode 100644
index 01e3272..0000000
--- a/3rdparty/libprocess/src/subprocess_posix.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <string>
-
-#include <glog/logging.h>
-
-#include <process/future.hpp>
-#include <process/reap.hpp>
-#include <process/subprocess.hpp>
-
-#include <stout/error.hpp>
-#include <stout/lambda.hpp>
-#include <stout/foreach.hpp>
-#include <stout/option.hpp>
-#include <stout/os.hpp>
-#include <stout/os/strerror.hpp>
-#include <stout/strings.hpp>
-#include <stout/try.hpp>
-
-using std::map;
-using std::string;
-using std::vector;
-
-namespace process {
-
-using InputFileDescriptors = Subprocess::IO::InputFileDescriptors;
-using OutputFileDescriptors = Subprocess::IO::OutputFileDescriptors;
-
-
-Subprocess::IO Subprocess::PIPE()
-{
-  return Subprocess::IO(
-      []() -> Try<InputFileDescriptors> {
-        int pipefd[2];
-        if (::pipe(pipefd) == -1) {
-          return ErrnoError("Failed to create pipe");
-        }
-
-        InputFileDescriptors fds;
-        fds.read = pipefd[0];
-        fds.write = pipefd[1];
-        return fds;
-      },
-      []() -> Try<OutputFileDescriptors> {
-        int pipefd[2];
-        if (::pipe(pipefd) == -1) {
-          return ErrnoError("Failed to create pipe");
-        }
-
-        OutputFileDescriptors fds;
-        fds.read = pipefd[0];
-        fds.write = pipefd[1];
-        return fds;
-      });
-}
-
-
-Subprocess::IO Subprocess::PATH(const string& path)
-{
-  return Subprocess::IO(
-      [path]() -> Try<InputFileDescriptors> {
-        Try<int> open = os::open(path, O_RDONLY | O_CLOEXEC);
-        if (open.isError()) {
-          return Error("Failed to open '" + path + "': " + open.error());
-        }
-
-        InputFileDescriptors fds;
-        fds.read = open.get();
-        return fds;
-      },
-      [path]() -> Try<OutputFileDescriptors> {
-        Try<int> open = os::open(
-            path,
-            O_WRONLY | O_CREAT | O_APPEND | O_CLOEXEC,
-            S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
-
-        if (open.isError()) {
-          return Error("Failed to open '" + path + "': " + open.error());
-        }
-
-        OutputFileDescriptors fds;
-        fds.write = open.get();
-        return fds;
-      });
-}
-
-}  // namespace process {

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/subprocess_posix.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/subprocess_posix.hpp b/3rdparty/libprocess/src/subprocess_posix.hpp
deleted file mode 100644
index 007058b..0000000
--- a/3rdparty/libprocess/src/subprocess_posix.hpp
+++ /dev/null
@@ -1,355 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License
-
-#ifndef __PROCESS_POSIX_SUBPROCESS_HPP__
-#define __PROCESS_POSIX_SUBPROCESS_HPP__
-
-#ifdef __linux__
-#include <sys/prctl.h>
-#endif // __linux__
-#include <sys/types.h>
-
-#include <string>
-
-#include <glog/logging.h>
-
-#include <process/subprocess.hpp>
-
-#include <stout/check.hpp>
-#include <stout/error.hpp>
-#include <stout/exit.hpp>
-#include <stout/foreach.hpp>
-#include <stout/hashset.hpp>
-#include <stout/nothing.hpp>
-#include <stout/lambda.hpp>
-#include <stout/none.hpp>
-#include <stout/option.hpp>
-#include <stout/os.hpp>
-#include <stout/try.hpp>
-#include <stout/unreachable.hpp>
-
-#include <stout/os/close.hpp>
-#include <stout/os/environment.hpp>
-#include <stout/os/fcntl.hpp>
-#include <stout/os/signals.hpp>
-#include <stout/os/strerror.hpp>
-
-namespace process {
-namespace internal {
-
-static void close(std::initializer_list<int_fd> fds);
-
-
-static void close(
-    const Subprocess::IO::InputFileDescriptors& stdinfds,
-    const Subprocess::IO::OutputFileDescriptors& stdoutfds,
-    const Subprocess::IO::OutputFileDescriptors& stderrfds);
-
-
-inline pid_t defaultClone(const lambda::function<int()>& func)
-{
-  pid_t pid = ::fork();
-  if (pid == -1) {
-    return -1;
-  } else if (pid == 0) {
-    // Child.
-    ::exit(func());
-    UNREACHABLE();
-  } else {
-    // Parent.
-    return pid;
-  }
-}
-
-
-// This function will invoke `os::cloexec` on all specified file
-// descriptors that are valid (i.e., not `None` and >= 0).
-inline Try<Nothing> cloexec(
-    const InputFileDescriptors& stdinfds,
-    const OutputFileDescriptors& stdoutfds,
-    const OutputFileDescriptors& stderrfds)
-{
-  hashset<int> fds = {
-    stdinfds.read,
-    stdinfds.write.getOrElse(-1),
-    stdoutfds.read.getOrElse(-1),
-    stdoutfds.write,
-    stderrfds.read.getOrElse(-1),
-    stderrfds.write
-  };
-
-  foreach (int fd, fds) {
-    if (fd >= 0) {
-      Try<Nothing> cloexec = os::cloexec(fd);
-      if (cloexec.isError()) {
-        return Error(cloexec.error());
-      }
-    }
-  }
-
-  return Nothing();
-}
-
-
-// The main entry of the child process.
-//
-// NOTE: This function has to be async signal safe.
-inline int childMain(
-    const std::string& path,
-    char** argv,
-    char** envp,
-    const InputFileDescriptors& stdinfds,
-    const OutputFileDescriptors& stdoutfds,
-    const OutputFileDescriptors& stderrfds,
-    bool blocking,
-    int pipes[2],
-    const std::vector<Subprocess::ChildHook>& child_hooks)
-{
-  // Close parent's end of the pipes.
-  if (stdinfds.write.isSome()) {
-    ::close(stdinfds.write.get());
-  }
-  if (stdoutfds.read.isSome()) {
-    ::close(stdoutfds.read.get());
-  }
-  if (stderrfds.read.isSome()) {
-    ::close(stderrfds.read.get());
-  }
-
-  // Currently we will block the child's execution of the new process
-  // until all the parent hooks (if any) have executed.
-  if (blocking) {
-    ::close(pipes[1]);
-  }
-
-  // Redirect I/O for stdin/stdout/stderr.
-  while (::dup2(stdinfds.read, STDIN_FILENO) == -1 && errno == EINTR);
-  while (::dup2(stdoutfds.write, STDOUT_FILENO) == -1 && errno == EINTR);
-  while (::dup2(stderrfds.write, STDERR_FILENO) == -1 && errno == EINTR);
-
-  // Close the copies. We need to make sure that we do not close the
-  // file descriptor assigned to stdin/stdout/stderr in case the
-  // parent has closed stdin/stdout/stderr when calling this
-  // function (in that case, a dup'ed file descriptor may have the
-  // same file descriptor number as stdin/stdout/stderr).
-  //
-  // We also need to ensure that we don't "double close" any file
-  // descriptors in the case where one of stdinfds.read,
-  // stdoutfds.write, or stdoutfds.write are equal.
-  if (stdinfds.read != STDIN_FILENO &&
-      stdinfds.read != STDOUT_FILENO &&
-      stdinfds.read != STDERR_FILENO) {
-    ::close(stdinfds.read);
-  }
-  if (stdoutfds.write != STDIN_FILENO &&
-      stdoutfds.write != STDOUT_FILENO &&
-      stdoutfds.write != STDERR_FILENO &&
-      stdoutfds.write != stdinfds.read) {
-    ::close(stdoutfds.write);
-  }
-  if (stderrfds.write != STDIN_FILENO &&
-      stderrfds.write != STDOUT_FILENO &&
-      stderrfds.write != STDERR_FILENO &&
-      stderrfds.write != stdinfds.read &&
-      stderrfds.write != stdoutfds.write) {
-    ::close(stderrfds.write);
-  }
-
-  if (blocking) {
-    // Do a blocking read on the pipe until the parent signals us to
-    // continue.
-    char dummy;
-    ssize_t length;
-    while ((length = ::read(pipes[0], &dummy, sizeof(dummy))) == -1 &&
-          errno == EINTR);
-
-    if (length != sizeof(dummy)) {
-      ABORT("Failed to synchronize with parent");
-    }
-
-    // Now close the pipe as we don't need it anymore.
-    ::close(pipes[0]);
-  }
-
-  // Run the child hooks.
-  foreach (const Subprocess::ChildHook& hook, child_hooks) {
-    Try<Nothing> callback = hook();
-
-    // If the callback failed, we should abort execution.
-    if (callback.isError()) {
-      ABORT("Failed to execute Subprocess::ChildHook: " + callback.error());
-    }
-  }
-
-  os::execvpe(path.c_str(), argv, envp);
-
-  SAFE_EXIT(
-      errno, "Failed to os::execvpe on path '%s': %d", path.c_str(), errno);
-}
-
-
-inline Try<pid_t> cloneChild(
-    const std::string& path,
-    std::vector<std::string> argv,
-    const Option<std::map<std::string, std::string>>& environment,
-    const Option<lambda::function<
-        pid_t(const lambda::function<int()>&)>>& _clone,
-    const std::vector<Subprocess::ParentHook>& parent_hooks,
-    const std::vector<Subprocess::ChildHook>& child_hooks,
-    const InputFileDescriptors stdinfds,
-    const OutputFileDescriptors stdoutfds,
-    const OutputFileDescriptors stderrfds)
-{
-  // The real arguments that will be passed to 'os::execvpe'. We need
-  // to construct them here before doing the clone as it might not be
-  // async signal safe to perform the memory allocation.
-  char** _argv = new char*[argv.size() + 1];
-  for (size_t i = 0; i < argv.size(); i++) {
-    _argv[i] = (char*) argv[i].c_str();
-  }
-  _argv[argv.size()] = nullptr;
-
-  // Like above, we need to construct the environment that we'll pass
-  // to 'os::execvpe' as it might not be async-safe to perform the
-  // memory allocations.
-  char** envp = os::raw::environment();
-
-  if (environment.isSome()) {
-    // NOTE: We add 1 to the size for a `nullptr` terminator.
-    envp = new char*[environment->size() + 1];
-
-    size_t index = 0;
-    foreachpair (
-        const std::string& key,
-        const std::string& value, environment.get()) {
-      std::string entry = key + "=" + value;
-      envp[index] = new char[entry.size() + 1];
-      strncpy(envp[index], entry.c_str(), entry.size() + 1);
-      ++index;
-    }
-
-    envp[index] = nullptr;
-  }
-
-  // Determine the function to clone the child process. If the user
-  // does not specify the clone function, we will use the default.
-  lambda::function<pid_t(const lambda::function<int()>&)> clone =
-    (_clone.isSome() ? _clone.get() : defaultClone);
-
-  // Currently we will block the child's execution of the new process
-  // until all the `parent_hooks` (if any) have executed.
-  std::array<int, 2> pipes;
-  const bool blocking = !parent_hooks.empty();
-
-  if (blocking) {
-    // We assume this should not fail under reasonable conditions so we
-    // use CHECK.
-    Try<std::array<int, 2>> pipe = os::pipe();
-    CHECK_SOME(pipe);
-
-    pipes = pipe.get();
-  }
-
-  // Now, clone the child process.
-  pid_t pid = clone(lambda::bind(
-      &childMain,
-      path,
-      _argv,
-      envp,
-      stdinfds,
-      stdoutfds,
-      stderrfds,
-      blocking,
-      pipes.data(),
-      child_hooks));
-
-  delete[] _argv;
-
-  // Need to delete 'envp' if we had environment variables passed to
-  // us and we needed to allocate the space.
-  if (environment.isSome()) {
-    CHECK_NE(os::raw::environment(), envp);
-
-    // We ignore the last 'envp' entry since it is nullptr.
-    for (size_t index = 0; index < environment->size(); index++) {
-      delete[] envp[index];
-    }
-
-    delete[] envp;
-  }
-
-  if (pid == -1) {
-    // Save the errno as 'close' below might overwrite it.
-    ErrnoError error("Failed to clone");
-    internal::close(stdinfds, stdoutfds, stderrfds);
-
-    if (blocking) {
-      os::close(pipes[0]);
-      os::close(pipes[1]);
-    }
-
-    return error;
-  }
-
-  // Close the child-ends of the file descriptors that are created by
-  // this function.
-  internal::close({stdinfds.read, stdoutfds.write, stderrfds.write});
-
-  if (blocking) {
-    os::close(pipes[0]);
-
-    // Run the parent hooks.
-    foreach (const Subprocess::ParentHook& hook, parent_hooks) {
-      Try<Nothing> parentSetup = hook.parent_setup(pid);
-
-      // If the hook callback fails, we shouldn't proceed with the
-      // execution and hence the child process should be killed.
-      if (parentSetup.isError()) {
-        LOG(WARNING)
-          << "Failed to execute Subprocess::ParentHook in parent for child '"
-          << pid << "': " << parentSetup.error();
-
-        os::close(pipes[1]);
-
-        // Ensure the child is killed.
-        ::kill(pid, SIGKILL);
-
-        return Error(
-            "Failed to execute Subprocess::ParentHook in parent for child '" +
-            stringify(pid) + "': " + parentSetup.error());
-      }
-    }
-
-    // Now that we've executed the parent hooks, we can signal the child to
-    // continue by writing to the pipe.
-    char dummy;
-    ssize_t length;
-    while ((length = ::write(pipes[1], &dummy, sizeof(dummy))) == -1 &&
-           errno == EINTR);
-
-    os::close(pipes[1]);
-
-    if (length != sizeof(dummy)) {
-      // Ensure the child is killed.
-      ::kill(pid, SIGKILL);
-
-      return Error("Failed to synchronize child process");
-    }
-  }
-
-  return pid;
-}
-
-}  // namespace internal {
-}  // namespace process {
-
-#endif // __PROCESS_POSIX_SUBPROCESS_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/subprocess_windows.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/subprocess_windows.cpp b/3rdparty/libprocess/src/subprocess_windows.cpp
deleted file mode 100644
index c497a03..0000000
--- a/3rdparty/libprocess/src/subprocess_windows.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/types.h>
-
-#include <string>
-
-#include <glog/logging.h>
-
-#include <process/future.hpp>
-#include <process/reap.hpp>
-#include <process/subprocess.hpp>
-
-#include <stout/error.hpp>
-#include <stout/foreach.hpp>
-#include <stout/lambda.hpp>
-#include <stout/option.hpp>
-#include <stout/os.hpp>
-#include <stout/strings.hpp>
-#include <stout/try.hpp>
-#include <stout/windows.hpp>
-
-#include <stout/os/int_fd.hpp>
-#include <stout/os/open.hpp>
-#include <stout/os/pipe.hpp>
-#include <stout/os/strerror.hpp>
-
-using std::array;
-using std::string;
-
-namespace process {
-
-using InputFileDescriptors = Subprocess::IO::InputFileDescriptors;
-using OutputFileDescriptors = Subprocess::IO::OutputFileDescriptors;
-
-// Opens a pair of file handles. On success, the first handle returned receives
-// the 'read' handle of the pipe, while the second receives the 'write' handle.
-// The pipe handles can then be passed to a child process, as shown in [1].
-//
-// [1] https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx
-Subprocess::IO Subprocess::PIPE()
-{
-  return Subprocess::IO(
-      []() -> Try<InputFileDescriptors> {
-        // Create STDIN pipe and set the 'read' component to be not overlapped,
-        // because we're sending it to the child process.
-        const Try<array<int_fd, 2>> pipefd = os::pipe(false, true);
-        if (pipefd.isError()) {
-          return Error(pipefd.error());
-        }
-
-        return InputFileDescriptors{pipefd.get()[0], pipefd.get()[1]};
-      },
-      []() -> Try<OutputFileDescriptors> {
-        // Create STDOUT pipe and set the 'write' component to be not
-        // overlapped, because we're sending it to the child process.
-        const Try<array<int_fd, 2>> pipefd = os::pipe(true, false);
-        if (pipefd.isError()) {
-          return Error(pipefd.error());
-        }
-
-        return OutputFileDescriptors{pipefd.get()[0], pipefd.get()[1]};
-      });
-}
-
-
-Subprocess::IO Subprocess::PATH(const string& path)
-{
-  return Subprocess::IO(
-      [path]() -> Try<InputFileDescriptors> {
-        const Try<int_fd> open = os::open(path, O_RDONLY);
-
-        if (open.isError()) {
-          return Error(open.error());
-        }
-
-        return InputFileDescriptors{open.get(), None()};
-      },
-      [path]() -> Try<OutputFileDescriptors> {
-        const Try<int_fd> open = os::open(path, O_WRONLY | O_CREAT | O_APPEND);
-
-        if (open.isError()) {
-          return Error(open.error());
-        }
-
-        return OutputFileDescriptors{None(), open.get()};
-      });
-}
-
-}  // namespace process {

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/subprocess_windows.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/subprocess_windows.hpp b/3rdparty/libprocess/src/subprocess_windows.hpp
deleted file mode 100644
index 1bbb8af..0000000
--- a/3rdparty/libprocess/src/subprocess_windows.hpp
+++ /dev/null
@@ -1,118 +0,0 @@
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License
-
-#ifndef __PROCESS_WINDOWS_SUBPROCESS_HPP__
-#define __PROCESS_WINDOWS_SUBPROCESS_HPP__
-
-#include <signal.h>
-
-#include <array>
-#include <string>
-
-#include <glog/logging.h>
-
-#include <process/subprocess.hpp>
-
-#include <stout/error.hpp>
-#include <stout/foreach.hpp>
-#include <stout/hashset.hpp>
-#include <stout/option.hpp>
-#include <stout/os.hpp>
-#include <stout/os/shell.hpp>
-#include <stout/try.hpp>
-#include <stout/windows.hpp>
-
-#include <stout/os/close.hpp>
-#include <stout/os/environment.hpp>
-
-#include <userEnv.h>
-
-namespace process {
-namespace internal {
-
-// NOTE: We are expecting that components of `argv` that need to be quoted
-// (for example, paths with spaces in them like `C:\"Program Files"\foo.exe`)
-// to have been already quoted correctly before we generate `command`.
-// Incorrectly-quoted command arguments will probably lead the child process
-// to terminate with an error. See also NOTE on `process::subprocess`.
-inline Try<::internal::windows::ProcessData> createChildProcess(
-    const std::string& path,
-    const std::vector<std::string>& argv,
-    const Option<std::map<std::string, std::string>>& environment,
-    const std::vector<Subprocess::ParentHook>& parent_hooks,
-    const InputFileDescriptors& stdinfds,
-    const OutputFileDescriptors& stdoutfds,
-    const OutputFileDescriptors& stderrfds,
-    const std::vector<int_fd>& whitelist_fds = {})
-{
-  const std::array<int_fd, 3> fds{
-    stdinfds.read, stdoutfds.write, stderrfds.write};
-
-  Try<::internal::windows::ProcessData> process_data =
-    ::internal::windows::create_process(
-        path,
-        argv,
-        environment,
-        true, // Create suspended.
-        fds,
-        whitelist_fds);
-
-  // Close the child-ends of the file descriptors that are created
-  // by this function.
-  foreach (const int_fd& fd, fds) {
-    if (fd.is_valid()) {
-      Try<Nothing> result = os::close(fd);
-      if (result.isError()) {
-        return Error(result.error());
-      }
-    }
-  }
-
-  if (process_data.isError()) {
-    return Error(process_data.error());
-  }
-
-  // Run the parent hooks.
-  const pid_t pid = process_data->pid;
-  foreach (const Subprocess::ParentHook& hook, parent_hooks) {
-    Try<Nothing> parent_setup = hook.parent_setup(pid);
-
-    // If the hook callback fails, we shouldn't proceed with the
-    // execution and hence the child process should be killed.
-    if (parent_setup.isError()) {
-      // Attempt to kill the process. Since it is still in suspended state, we
-      // do not need to kill any descendents. We also can't use `os::kill_job`
-      // because this process is not in a Job Object unless one of the parent
-      // hooks added it.
-      ::TerminateProcess(process_data->process_handle.get_handle(), 1);
-
-      return Error(
-          "Failed to execute Parent Hook in child '" + stringify(pid) +
-          "' with command '" + stringify(argv) + "': " +
-          parent_setup.error());
-    }
-  }
-
-  // Start child process.
-  if (::ResumeThread(process_data->thread_handle.get_handle()) == -1) {
-    return WindowsError(
-        "Failed to resume child process with command '" +
-        stringify(argv) + "'");
-  }
-
-  return process_data;
-}
-
-}  // namespace internal {
-}  // namespace process {
-
-#endif // __PROCESS_WINDOWS_SUBPROCESS_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/windows/subprocess.cpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/windows/subprocess.cpp b/3rdparty/libprocess/src/windows/subprocess.cpp
new file mode 100644
index 0000000..c497a03
--- /dev/null
+++ b/3rdparty/libprocess/src/windows/subprocess.cpp
@@ -0,0 +1,101 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <string>
+
+#include <glog/logging.h>
+
+#include <process/future.hpp>
+#include <process/reap.hpp>
+#include <process/subprocess.hpp>
+
+#include <stout/error.hpp>
+#include <stout/foreach.hpp>
+#include <stout/lambda.hpp>
+#include <stout/option.hpp>
+#include <stout/os.hpp>
+#include <stout/strings.hpp>
+#include <stout/try.hpp>
+#include <stout/windows.hpp>
+
+#include <stout/os/int_fd.hpp>
+#include <stout/os/open.hpp>
+#include <stout/os/pipe.hpp>
+#include <stout/os/strerror.hpp>
+
+using std::array;
+using std::string;
+
+namespace process {
+
+using InputFileDescriptors = Subprocess::IO::InputFileDescriptors;
+using OutputFileDescriptors = Subprocess::IO::OutputFileDescriptors;
+
+// Opens a pair of file handles. On success, the first handle returned receives
+// the 'read' handle of the pipe, while the second receives the 'write' handle.
+// The pipe handles can then be passed to a child process, as shown in [1].
+//
+// [1] https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx
+Subprocess::IO Subprocess::PIPE()
+{
+  return Subprocess::IO(
+      []() -> Try<InputFileDescriptors> {
+        // Create STDIN pipe and set the 'read' component to be not overlapped,
+        // because we're sending it to the child process.
+        const Try<array<int_fd, 2>> pipefd = os::pipe(false, true);
+        if (pipefd.isError()) {
+          return Error(pipefd.error());
+        }
+
+        return InputFileDescriptors{pipefd.get()[0], pipefd.get()[1]};
+      },
+      []() -> Try<OutputFileDescriptors> {
+        // Create STDOUT pipe and set the 'write' component to be not
+        // overlapped, because we're sending it to the child process.
+        const Try<array<int_fd, 2>> pipefd = os::pipe(true, false);
+        if (pipefd.isError()) {
+          return Error(pipefd.error());
+        }
+
+        return OutputFileDescriptors{pipefd.get()[0], pipefd.get()[1]};
+      });
+}
+
+
+Subprocess::IO Subprocess::PATH(const string& path)
+{
+  return Subprocess::IO(
+      [path]() -> Try<InputFileDescriptors> {
+        const Try<int_fd> open = os::open(path, O_RDONLY);
+
+        if (open.isError()) {
+          return Error(open.error());
+        }
+
+        return InputFileDescriptors{open.get(), None()};
+      },
+      [path]() -> Try<OutputFileDescriptors> {
+        const Try<int_fd> open = os::open(path, O_WRONLY | O_CREAT | O_APPEND);
+
+        if (open.isError()) {
+          return Error(open.error());
+        }
+
+        return OutputFileDescriptors{None(), open.get()};
+      });
+}
+
+}  // namespace process {

http://git-wip-us.apache.org/repos/asf/mesos/blob/78244b53/3rdparty/libprocess/src/windows/subprocess.hpp
----------------------------------------------------------------------
diff --git a/3rdparty/libprocess/src/windows/subprocess.hpp b/3rdparty/libprocess/src/windows/subprocess.hpp
new file mode 100644
index 0000000..1bbb8af
--- /dev/null
+++ b/3rdparty/libprocess/src/windows/subprocess.hpp
@@ -0,0 +1,118 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License
+
+#ifndef __PROCESS_WINDOWS_SUBPROCESS_HPP__
+#define __PROCESS_WINDOWS_SUBPROCESS_HPP__
+
+#include <signal.h>
+
+#include <array>
+#include <string>
+
+#include <glog/logging.h>
+
+#include <process/subprocess.hpp>
+
+#include <stout/error.hpp>
+#include <stout/foreach.hpp>
+#include <stout/hashset.hpp>
+#include <stout/option.hpp>
+#include <stout/os.hpp>
+#include <stout/os/shell.hpp>
+#include <stout/try.hpp>
+#include <stout/windows.hpp>
+
+#include <stout/os/close.hpp>
+#include <stout/os/environment.hpp>
+
+#include <userEnv.h>
+
+namespace process {
+namespace internal {
+
+// NOTE: We are expecting that components of `argv` that need to be quoted
+// (for example, paths with spaces in them like `C:\"Program Files"\foo.exe`)
+// to have been already quoted correctly before we generate `command`.
+// Incorrectly-quoted command arguments will probably lead the child process
+// to terminate with an error. See also NOTE on `process::subprocess`.
+inline Try<::internal::windows::ProcessData> createChildProcess(
+    const std::string& path,
+    const std::vector<std::string>& argv,
+    const Option<std::map<std::string, std::string>>& environment,
+    const std::vector<Subprocess::ParentHook>& parent_hooks,
+    const InputFileDescriptors& stdinfds,
+    const OutputFileDescriptors& stdoutfds,
+    const OutputFileDescriptors& stderrfds,
+    const std::vector<int_fd>& whitelist_fds = {})
+{
+  const std::array<int_fd, 3> fds{
+    stdinfds.read, stdoutfds.write, stderrfds.write};
+
+  Try<::internal::windows::ProcessData> process_data =
+    ::internal::windows::create_process(
+        path,
+        argv,
+        environment,
+        true, // Create suspended.
+        fds,
+        whitelist_fds);
+
+  // Close the child-ends of the file descriptors that are created
+  // by this function.
+  foreach (const int_fd& fd, fds) {
+    if (fd.is_valid()) {
+      Try<Nothing> result = os::close(fd);
+      if (result.isError()) {
+        return Error(result.error());
+      }
+    }
+  }
+
+  if (process_data.isError()) {
+    return Error(process_data.error());
+  }
+
+  // Run the parent hooks.
+  const pid_t pid = process_data->pid;
+  foreach (const Subprocess::ParentHook& hook, parent_hooks) {
+    Try<Nothing> parent_setup = hook.parent_setup(pid);
+
+    // If the hook callback fails, we shouldn't proceed with the
+    // execution and hence the child process should be killed.
+    if (parent_setup.isError()) {
+      // Attempt to kill the process. Since it is still in suspended state, we
+      // do not need to kill any descendents. We also can't use `os::kill_job`
+      // because this process is not in a Job Object unless one of the parent
+      // hooks added it.
+      ::TerminateProcess(process_data->process_handle.get_handle(), 1);
+
+      return Error(
+          "Failed to execute Parent Hook in child '" + stringify(pid) +
+          "' with command '" + stringify(argv) + "': " +
+          parent_setup.error());
+    }
+  }
+
+  // Start child process.
+  if (::ResumeThread(process_data->thread_handle.get_handle()) == -1) {
+    return WindowsError(
+        "Failed to resume child process with command '" +
+        stringify(argv) + "'");
+  }
+
+  return process_data;
+}
+
+}  // namespace internal {
+}  // namespace process {
+
+#endif // __PROCESS_WINDOWS_SUBPROCESS_HPP__