You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by id...@apache.org on 2014/04/29 19:16:48 UTC

git commit: Rename CgroupsLauncher to LinuxLauncher.

Repository: mesos
Updated Branches:
  refs/heads/master 12511606a -> b1b2b4cc9


Rename CgroupsLauncher to LinuxLauncher.

This is a rename only; there are no code changes but this facilitates a
follow on review which will modify the launcher to support both Linux
cgroups and Linux namespaces.

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


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

Branch: refs/heads/master
Commit: b1b2b4cc9d2c45f953c9c97318f0c4f4955fc174
Parents: 1251160
Author: Ian Downes <ia...@gmail.com>
Authored: Tue Apr 29 10:14:19 2014 -0700
Committer: Ian Downes <id...@twitter.com>
Committed: Tue Apr 29 10:14:19 2014 -0700

----------------------------------------------------------------------
 src/Makefile.am                              |   4 +-
 src/slave/containerizer/cgroups_launcher.cpp | 269 ----------------------
 src/slave/containerizer/cgroups_launcher.hpp |  64 -----
 src/slave/containerizer/containerizer.cpp    |   4 +-
 src/slave/containerizer/linux_launcher.cpp   | 269 ++++++++++++++++++++++
 src/slave/containerizer/linux_launcher.hpp   |  64 +++++
 src/tests/isolator_tests.cpp                 |   8 +-
 7 files changed, 341 insertions(+), 341 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/mesos/blob/b1b2b4cc/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index cd06125..cb95f7c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -254,7 +254,7 @@ if OS_LINUX
   libmesos_no_3rdparty_la_SOURCES += linux/cgroups.cpp
   libmesos_no_3rdparty_la_SOURCES += slave/containerizer/isolators/cgroups/cpushare.cpp
   libmesos_no_3rdparty_la_SOURCES += slave/containerizer/isolators/cgroups/mem.cpp
-  libmesos_no_3rdparty_la_SOURCES += slave/containerizer/cgroups_launcher.cpp
+  libmesos_no_3rdparty_la_SOURCES += slave/containerizer/linux_launcher.cpp
   libmesos_no_3rdparty_la_SOURCES += linux/fs.cpp
 else
   EXTRA_DIST += linux/cgroups.cpp
@@ -283,7 +283,7 @@ libmesos_no_3rdparty_la_SOURCES += common/attributes.hpp		\
 	master/registrar.hpp						\
 	master/master.hpp master/sorter.hpp				\
 	messages/messages.hpp slave/constants.hpp			\
-	slave/containerizer/cgroups_launcher.hpp			\
+	slave/containerizer/linux_launcher.hpp				\
 	slave/containerizer/containerizer.hpp				\
 	slave/containerizer/isolator.hpp				\
 	slave/containerizer/isolators/cgroups/cpushare.hpp		\

http://git-wip-us.apache.org/repos/asf/mesos/blob/b1b2b4cc/src/slave/containerizer/cgroups_launcher.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/cgroups_launcher.cpp b/src/slave/containerizer/cgroups_launcher.cpp
deleted file mode 100644
index 39f0e4c..0000000
--- a/src/slave/containerizer/cgroups_launcher.cpp
+++ /dev/null
@@ -1,269 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <vector>
-
-#include <stout/abort.hpp>
-#include <stout/hashset.hpp>
-#include <stout/path.hpp>
-#include <stout/unreachable.hpp>
-
-#include "linux/cgroups.hpp"
-
-#include "mesos/resources.hpp"
-
-#include "slave/containerizer/cgroups_launcher.hpp"
-
-using namespace process;
-
-using std::list;
-using std::string;
-using std::vector;
-
-namespace mesos {
-namespace internal {
-namespace slave {
-
-using state::RunState;
-
-CgroupsLauncher::CgroupsLauncher(const Flags& _flags, const string& _hierarchy)
-  : flags(_flags),
-    hierarchy(_hierarchy) {}
-
-
-Try<Launcher*> CgroupsLauncher::create(const Flags& flags)
-{
-  Try<string> hierarchy = cgroups::prepare(
-      flags.cgroups_hierarchy, "freezer", flags.cgroups_root);
-
-  if (hierarchy.isError()) {
-    return Error("Failed to create cgroups launcher: " + hierarchy.error());
-  }
-
-  LOG(INFO) << "Using " << hierarchy.get()
-            << " as the freezer hierarchy for the cgroups launcher";
-
-  return new CgroupsLauncher(flags, hierarchy.get());
-}
-
-
-Try<Nothing> CgroupsLauncher::recover(const std::list<state::RunState>& states)
-{
-  hashset<string> cgroups;
-
-  foreach (const RunState& state, states) {
-    if (state.id.isNone()) {
-      return Error("ContainerID is required to recover");
-    }
-    const ContainerID& containerId = state.id.get();
-
-    Try<bool> exists = cgroups::exists(hierarchy, cgroup(containerId));
-
-    if (!exists.get()) {
-      // This may occur if the freezer cgroup was destroyed but the slave dies
-      // before noticing this.
-      // The containerizer will monitor the container's pid and notice that it
-      // has exited, triggering destruction of the container.
-      LOG(INFO) << "Couldn't find freezer cgroup for container " << containerId;
-      continue;
-    }
-
-    if (state.forkedPid.isNone()) {
-      return Error("Executor pid is required to recover container " +
-                   stringify(containerId));
-    }
-    pid_t pid = state.forkedPid.get();
-
-    if (pids.containsValue(pid)) {
-      // This should (almost) never occur. There is the possibility that a new
-      // executor is launched with the same pid as one that just exited (highly
-      // unlikely) and the slave dies after the new executor is launched but
-      // before it hears about the termination of the earlier executor (also
-      // unlikely). Regardless, the launcher can't do anything sensible so this
-      // is considered an error.
-      return Error("Detected duplicate pid " + stringify(pid) +
-                   " for container " + stringify(containerId));
-    }
-
-    pids.put(containerId, pid);
-
-    cgroups.insert(cgroup(containerId));
-  }
-
-  Try<vector<string> > orphans = cgroups::get(hierarchy, flags.cgroups_root);
-  if (orphans.isError()) {
-    return Error(orphans.error());
-  }
-
-  foreach (const string& orphan, orphans.get()) {
-    if (!cgroups.contains(orphan)) {
-      LOG(INFO) << "Removing orphaned cgroup"
-                << " '" << path::join("freezer", orphan) << "'";
-      cgroups::destroy(hierarchy, orphan);
-    }
-  }
-
-  return Nothing();
-}
-
-
-Try<pid_t> CgroupsLauncher::fork(
-    const ContainerID& containerId,
-    const lambda::function<int()>& inChild)
-{
-  // Create a freezer cgroup for this container if necessary.
-  Try<bool> exists = cgroups::exists(hierarchy, cgroup(containerId));
-
-  if (exists.isError()) {
-    return Error("Failed to create freezer cgroup: " + exists.error());
-  }
-
-  if (!exists.get()) {
-    Try<Nothing> created = cgroups::create(hierarchy, cgroup(containerId));
-
-    if (created.isError()) {
-      LOG(ERROR) << "Failed to create freezer cgroup for container '"
-                 << containerId << "': " << created.error();
-      return Error("Failed to contain process: " + created.error());
-    }
-  }
-
-  // Additional processes forked will be put into the same process group and
-  // session.
-  Option<pid_t> pgid = pids.get(containerId);
-
-  // Use a pipe to block the child until it's been moved into the freezer
-  // cgroup.
-  int pipes[2];
-  // We assume this should not fail under reasonable conditions so we use CHECK.
-  CHECK(pipe(pipes) == 0);
-
-  pid_t pid;
-
-  if ((pid = ::fork()) == -1) {
-    return ErrnoError("Failed to fork");
-  }
-
-  if (pid == 0) {
-    // In child.
-    os::close(pipes[1]);
-
-    // Move to a previously created process group (and session) if available,
-    // else create a new session and process group. Even though we track
-    // processes using cgroups we need to move to a different session so we're
-    // independent from the slave's session (otherwise children will receive
-    // SIGHUP if the slave exits).
-    // TODO(idownes): perror is not listed as async-signal-safe and should be
-    // reimplemented safely.
-    if (pgid.isSome() && (setpgid(0, pgid.get()) == -1)) {
-      perror("Failed to put child into process group");
-      os::close(pipes[0]);
-      _exit(1);
-    } else if (setsid() == -1) {
-      perror("Failed to put child in a new session");
-      os::close(pipes[0]);
-      _exit(1);
-    }
-
-    // Do a blocking read on the pipe until the parent signals us to continue.
-    int buf;
-    int len;
-    while ((len = read(pipes[0], &buf, sizeof(buf))) == -1 && errno == EINTR);
-
-    if (len != sizeof(buf)) {
-      os::close(pipes[0]);
-      ABORT("Failed to synchronize with parent");
-    }
-
-    os::close(pipes[0]);
-
-    // This function should exec() and therefore not return.
-    inChild();
-
-    ABORT("Child failed to exec");
-  }
-
-  // Parent.
-  os::close(pipes[0]);
-
-  // Move the child into the freezer cgroup. Any grandchildren will also be
-  // contained in the cgroup.
-  Try<Nothing> assign = cgroups::assign(hierarchy, cgroup(containerId), pid);
-
-  if (assign.isError()) {
-    LOG(ERROR) << "Failed to assign process " << pid
-                << " of container '" << containerId << "'"
-                << " to its freezer cgroup: " << assign.error();
-    kill(pid, SIGKILL);
-    return Error("Failed to contain process");
-  }
-
-  // Now that we've contained the child we can signal it to continue by
-  // writing to the pipe.
-  int buf;
-  ssize_t len;
-  while ((len = write(pipes[1], &buf, sizeof(buf))) == -1 && errno == EINTR);
-
-  if (len != sizeof(buf)) {
-    // Ensure the child is killed.
-    kill(pid, SIGKILL);
-    os::close(pipes[1]);
-    return Error("Failed to synchronize child process");
-  }
-  os::close(pipes[1]);
-
-  // Store the pid (session id and process group id) if this is the first
-  // process forked for this container.
-  if (!pids.contains(containerId)) {
-    pids.put(containerId, pid);
-  }
-
-  return pid;
-}
-
-
-Future<Nothing> _destroy(
-    const ContainerID& containerId,
-    process::Future<bool> destroyed)
-{
-  if (destroyed.isFailed()) {
-    LOG(ERROR) << "Failed to destroy freezer cgroup for '"
-               << containerId << "': " << destroyed.failure();
-    return Failure("Failed to destroy launcher: " + destroyed.failure());
-  }
-  return Nothing();
-}
-
-
-Future<Nothing> CgroupsLauncher::destroy(const ContainerID& containerId)
-{
-  pids.erase(containerId);
-
-  return cgroups::destroy(hierarchy, cgroup(containerId))
-    .then(lambda::bind(&_destroy, containerId, lambda::_1));
-}
-
-
-string CgroupsLauncher::cgroup(const ContainerID& containerId)
-{
-  return path::join(flags.cgroups_root, containerId.value());
-}
-
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/b1b2b4cc/src/slave/containerizer/cgroups_launcher.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/cgroups_launcher.hpp b/src/slave/containerizer/cgroups_launcher.hpp
deleted file mode 100644
index db61107..0000000
--- a/src/slave/containerizer/cgroups_launcher.hpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef __CGROUPS_LAUNCHER_HPP__
-#define __CGROUPS_LAUNCHER_HPP__
-
-#include "slave/containerizer/launcher.hpp"
-
-namespace mesos {
-namespace internal {
-namespace slave {
-
-// Launcher for Linux systems with cgroups. Uses a freezer cgroup to track
-// pids.
-class CgroupsLauncher : public Launcher
-{
-public:
-  static Try<Launcher*> create(const Flags& flags);
-
-  virtual ~CgroupsLauncher() {}
-
-  virtual Try<Nothing> recover(const std::list<state::RunState>& states);
-
-  virtual Try<pid_t> fork(
-      const ContainerID& containerId,
-      const lambda::function<int()>& inChild);
-
-  virtual process::Future<Nothing> destroy(const ContainerID& containerId);
-
-private:
-  CgroupsLauncher(const Flags& flags, const std::string& hierarchy);
-
-  static const std::string subsystem;
-  const Flags flags;
-  const std::string hierarchy;
-
-  std::string cgroup(const ContainerID& containerId);
-
-  // The 'pid' is the process id of the first process and also the process
-  // group id and session id.
-  hashmap<ContainerID, pid_t> pids;
-};
-
-
-} // namespace slave {
-} // namespace internal {
-} // namespace mesos {
-
-#endif // __CGROUPS_LAUNCHER_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/b1b2b4cc/src/slave/containerizer/containerizer.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/containerizer.cpp b/src/slave/containerizer/containerizer.cpp
index 9321bbd..374a778 100644
--- a/src/slave/containerizer/containerizer.cpp
+++ b/src/slave/containerizer/containerizer.cpp
@@ -32,7 +32,7 @@
 #include "slave/slave.hpp"
 
 #ifdef __linux__
-#include "slave/containerizer/cgroups_launcher.hpp"
+#include "slave/containerizer/linux_launcher.hpp"
 #endif // __linux__
 #include "slave/containerizer/containerizer.hpp"
 #include "slave/containerizer/isolator.hpp"
@@ -210,7 +210,7 @@ Try<Containerizer*> Containerizer::create(
 #ifdef __linux__
   // Use cgroups on Linux if any cgroups isolators are used.
   Try<Launcher*> launcher = strings::contains(isolation, "cgroups")
-    ? CgroupsLauncher::create(flags) : PosixLauncher::create(flags);
+    ? LinuxLauncher::create(flags) : PosixLauncher::create(flags);
 #else
   Try<Launcher*> launcher = PosixLauncher::create(flags);
 #endif // __linux__

http://git-wip-us.apache.org/repos/asf/mesos/blob/b1b2b4cc/src/slave/containerizer/linux_launcher.cpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/linux_launcher.cpp b/src/slave/containerizer/linux_launcher.cpp
new file mode 100644
index 0000000..530e0bd
--- /dev/null
+++ b/src/slave/containerizer/linux_launcher.cpp
@@ -0,0 +1,269 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vector>
+
+#include <stout/abort.hpp>
+#include <stout/hashset.hpp>
+#include <stout/path.hpp>
+#include <stout/unreachable.hpp>
+
+#include "linux/cgroups.hpp"
+
+#include "mesos/resources.hpp"
+
+#include "slave/containerizer/linux_launcher.hpp"
+
+using namespace process;
+
+using std::list;
+using std::string;
+using std::vector;
+
+namespace mesos {
+namespace internal {
+namespace slave {
+
+using state::RunState;
+
+LinuxLauncher::LinuxLauncher(const Flags& _flags, const string& _hierarchy)
+  : flags(_flags),
+    hierarchy(_hierarchy) {}
+
+
+Try<Launcher*> LinuxLauncher::create(const Flags& flags)
+{
+  Try<string> hierarchy = cgroups::prepare(
+      flags.cgroups_hierarchy, "freezer", flags.cgroups_root);
+
+  if (hierarchy.isError()) {
+    return Error("Failed to create Linux launcher: " + hierarchy.error());
+  }
+
+  LOG(INFO) << "Using " << hierarchy.get()
+            << " as the freezer hierarchy for the Linux launcher";
+
+  return new LinuxLauncher(flags, hierarchy.get());
+}
+
+
+Try<Nothing> LinuxLauncher::recover(const std::list<state::RunState>& states)
+{
+  hashset<string> cgroups;
+
+  foreach (const RunState& state, states) {
+    if (state.id.isNone()) {
+      return Error("ContainerID is required to recover");
+    }
+    const ContainerID& containerId = state.id.get();
+
+    Try<bool> exists = cgroups::exists(hierarchy, cgroup(containerId));
+
+    if (!exists.get()) {
+      // This may occur if the freezer cgroup was destroyed but the slave dies
+      // before noticing this.
+      // The containerizer will monitor the container's pid and notice that it
+      // has exited, triggering destruction of the container.
+      LOG(INFO) << "Couldn't find freezer cgroup for container " << containerId;
+      continue;
+    }
+
+    if (state.forkedPid.isNone()) {
+      return Error("Executor pid is required to recover container " +
+                   stringify(containerId));
+    }
+    pid_t pid = state.forkedPid.get();
+
+    if (pids.containsValue(pid)) {
+      // This should (almost) never occur. There is the possibility that a new
+      // executor is launched with the same pid as one that just exited (highly
+      // unlikely) and the slave dies after the new executor is launched but
+      // before it hears about the termination of the earlier executor (also
+      // unlikely). Regardless, the launcher can't do anything sensible so this
+      // is considered an error.
+      return Error("Detected duplicate pid " + stringify(pid) +
+                   " for container " + stringify(containerId));
+    }
+
+    pids.put(containerId, pid);
+
+    cgroups.insert(cgroup(containerId));
+  }
+
+  Try<vector<string> > orphans = cgroups::get(hierarchy, flags.cgroups_root);
+  if (orphans.isError()) {
+    return Error(orphans.error());
+  }
+
+  foreach (const string& orphan, orphans.get()) {
+    if (!cgroups.contains(orphan)) {
+      LOG(INFO) << "Removing orphaned cgroup"
+                << " '" << path::join("freezer", orphan) << "'";
+      cgroups::destroy(hierarchy, orphan);
+    }
+  }
+
+  return Nothing();
+}
+
+
+Try<pid_t> LinuxLauncher::fork(
+    const ContainerID& containerId,
+    const lambda::function<int()>& inChild)
+{
+  // Create a freezer cgroup for this container if necessary.
+  Try<bool> exists = cgroups::exists(hierarchy, cgroup(containerId));
+
+  if (exists.isError()) {
+    return Error("Failed to create freezer cgroup: " + exists.error());
+  }
+
+  if (!exists.get()) {
+    Try<Nothing> created = cgroups::create(hierarchy, cgroup(containerId));
+
+    if (created.isError()) {
+      LOG(ERROR) << "Failed to create freezer cgroup for container '"
+                 << containerId << "': " << created.error();
+      return Error("Failed to contain process: " + created.error());
+    }
+  }
+
+  // Additional processes forked will be put into the same process group and
+  // session.
+  Option<pid_t> pgid = pids.get(containerId);
+
+  // Use a pipe to block the child until it's been moved into the freezer
+  // cgroup.
+  int pipes[2];
+  // We assume this should not fail under reasonable conditions so we use CHECK.
+  CHECK(pipe(pipes) == 0);
+
+  pid_t pid;
+
+  if ((pid = ::fork()) == -1) {
+    return ErrnoError("Failed to fork");
+  }
+
+  if (pid == 0) {
+    // In child.
+    os::close(pipes[1]);
+
+    // Move to a previously created process group (and session) if available,
+    // else create a new session and process group. Even though we track
+    // processes using cgroups we need to move to a different session so we're
+    // independent from the slave's session (otherwise children will receive
+    // SIGHUP if the slave exits).
+    // TODO(idownes): perror is not listed as async-signal-safe and should be
+    // reimplemented safely.
+    if (pgid.isSome() && (setpgid(0, pgid.get()) == -1)) {
+      perror("Failed to put child into process group");
+      os::close(pipes[0]);
+      _exit(1);
+    } else if (setsid() == -1) {
+      perror("Failed to put child in a new session");
+      os::close(pipes[0]);
+      _exit(1);
+    }
+
+    // Do a blocking read on the pipe until the parent signals us to continue.
+    int buf;
+    int len;
+    while ((len = read(pipes[0], &buf, sizeof(buf))) == -1 && errno == EINTR);
+
+    if (len != sizeof(buf)) {
+      os::close(pipes[0]);
+      ABORT("Failed to synchronize with parent");
+    }
+
+    os::close(pipes[0]);
+
+    // This function should exec() and therefore not return.
+    inChild();
+
+    ABORT("Child failed to exec");
+  }
+
+  // Parent.
+  os::close(pipes[0]);
+
+  // Move the child into the freezer cgroup. Any grandchildren will also be
+  // contained in the cgroup.
+  Try<Nothing> assign = cgroups::assign(hierarchy, cgroup(containerId), pid);
+
+  if (assign.isError()) {
+    LOG(ERROR) << "Failed to assign process " << pid
+                << " of container '" << containerId << "'"
+                << " to its freezer cgroup: " << assign.error();
+    kill(pid, SIGKILL);
+    return Error("Failed to contain process");
+  }
+
+  // Now that we've contained the child we can signal it to continue by
+  // writing to the pipe.
+  int buf;
+  ssize_t len;
+  while ((len = write(pipes[1], &buf, sizeof(buf))) == -1 && errno == EINTR);
+
+  if (len != sizeof(buf)) {
+    // Ensure the child is killed.
+    kill(pid, SIGKILL);
+    os::close(pipes[1]);
+    return Error("Failed to synchronize child process");
+  }
+  os::close(pipes[1]);
+
+  // Store the pid (session id and process group id) if this is the first
+  // process forked for this container.
+  if (!pids.contains(containerId)) {
+    pids.put(containerId, pid);
+  }
+
+  return pid;
+}
+
+
+Future<Nothing> _destroy(
+    const ContainerID& containerId,
+    process::Future<bool> destroyed)
+{
+  if (destroyed.isFailed()) {
+    LOG(ERROR) << "Failed to destroy freezer cgroup for '"
+               << containerId << "': " << destroyed.failure();
+    return Failure("Failed to destroy launcher: " + destroyed.failure());
+  }
+  return Nothing();
+}
+
+
+Future<Nothing> LinuxLauncher::destroy(const ContainerID& containerId)
+{
+  pids.erase(containerId);
+
+  return cgroups::destroy(hierarchy, cgroup(containerId))
+    .then(lambda::bind(&_destroy, containerId, lambda::_1));
+}
+
+
+string LinuxLauncher::cgroup(const ContainerID& containerId)
+{
+  return path::join(flags.cgroups_root, containerId.value());
+}
+
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {

http://git-wip-us.apache.org/repos/asf/mesos/blob/b1b2b4cc/src/slave/containerizer/linux_launcher.hpp
----------------------------------------------------------------------
diff --git a/src/slave/containerizer/linux_launcher.hpp b/src/slave/containerizer/linux_launcher.hpp
new file mode 100644
index 0000000..8f96c69
--- /dev/null
+++ b/src/slave/containerizer/linux_launcher.hpp
@@ -0,0 +1,64 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __LINUX_LAUNCHER_HPP__
+#define __LINUX_LAUNCHER_HPP__
+
+#include "slave/containerizer/launcher.hpp"
+
+namespace mesos {
+namespace internal {
+namespace slave {
+
+// Launcher for Linux systems with cgroups. Uses a freezer cgroup to track
+// pids.
+class LinuxLauncher : public Launcher
+{
+public:
+  static Try<Launcher*> create(const Flags& flags);
+
+  virtual ~LinuxLauncher() {}
+
+  virtual Try<Nothing> recover(const std::list<state::RunState>& states);
+
+  virtual Try<pid_t> fork(
+      const ContainerID& containerId,
+      const lambda::function<int()>& inChild);
+
+  virtual process::Future<Nothing> destroy(const ContainerID& containerId);
+
+private:
+  LinuxLauncher(const Flags& flags, const std::string& hierarchy);
+
+  static const std::string subsystem;
+  const Flags flags;
+  const std::string hierarchy;
+
+  std::string cgroup(const ContainerID& containerId);
+
+  // The 'pid' is the process id of the first process and also the process
+  // group id and session id.
+  hashmap<ContainerID, pid_t> pids;
+};
+
+
+} // namespace slave {
+} // namespace internal {
+} // namespace mesos {
+
+#endif // __LINUX_LAUNCHER_HPP__

http://git-wip-us.apache.org/repos/asf/mesos/blob/b1b2b4cc/src/tests/isolator_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/isolator_tests.cpp b/src/tests/isolator_tests.cpp
index dde977e..b0eff57 100644
--- a/src/tests/isolator_tests.cpp
+++ b/src/tests/isolator_tests.cpp
@@ -39,7 +39,7 @@
 #include "slave/slave.hpp"
 
 #ifdef __linux__
-#include "slave/containerizer/cgroups_launcher.hpp"
+#include "slave/containerizer/linux_launcher.hpp"
 #endif // __linux__
 #include "slave/containerizer/isolator.hpp"
 #include "slave/containerizer/launcher.hpp"
@@ -62,8 +62,8 @@ using namespace process;
 using mesos::internal::master::Master;
 #ifdef __linux__
 using mesos::internal::slave::CgroupsCpushareIsolatorProcess;
-using mesos::internal::slave::CgroupsLauncher;
 using mesos::internal::slave::CgroupsMemIsolatorProcess;
+using mesos::internal::slave::LinuxLauncher;
 #endif // __linux__
 using mesos::internal::slave::Isolator;
 using mesos::internal::slave::IsolatorProcess;
@@ -308,7 +308,7 @@ TEST_F(LimitedCpuIsolatorTest, ROOT_CGROUPS_Cfs)
   Try<Isolator*> isolator = CgroupsCpushareIsolatorProcess::create(flags);
   CHECK_SOME(isolator);
 
-  Try<Launcher*> launcher = CgroupsLauncher::create(flags);
+  Try<Launcher*> launcher = LinuxLauncher::create(flags);
   CHECK_SOME(launcher);
 
   // Set the executor's resources to 0.5 cpu.
@@ -395,7 +395,7 @@ TEST_F(LimitedCpuIsolatorTest, ROOT_CGROUPS_Cfs_Big_Quota)
   Try<Isolator*> isolator = CgroupsCpushareIsolatorProcess::create(flags);
   CHECK_SOME(isolator);
 
-  Try<Launcher*> launcher = CgroupsLauncher::create(flags);
+  Try<Launcher*> launcher = LinuxLauncher::create(flags);
   CHECK_SOME(launcher);
 
   // Set the executor's resources to 100.5 cpu.