You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by ji...@apache.org on 2014/07/16 18:34:44 UTC
git commit: Added a low level scheduler example using pthread.
Repository: mesos
Updated Branches:
refs/heads/master 11b6fd851 -> 049aed3c9
Added a low level scheduler example using pthread.
Review: https://reviews.apache.org/r/23216
Project: http://git-wip-us.apache.org/repos/asf/mesos/repo
Commit: http://git-wip-us.apache.org/repos/asf/mesos/commit/049aed3c
Tree: http://git-wip-us.apache.org/repos/asf/mesos/tree/049aed3c
Diff: http://git-wip-us.apache.org/repos/asf/mesos/diff/049aed3c
Branch: refs/heads/master
Commit: 049aed3c9c19405dade552f762b2741935223a16
Parents: 11b6fd8
Author: Zuyu Zhang <hi...@gmail.com>
Authored: Wed Jul 16 09:34:25 2014 -0700
Committer: Jie Yu <yu...@gmail.com>
Committed: Wed Jul 16 09:34:25 2014 -0700
----------------------------------------------------------------------
src/Makefile.am | 6 +
src/examples/low_level_scheduler_libprocess.cpp | 45 +-
src/examples/low_level_scheduler_pthread.cpp | 486 +++++++++++++++++++
src/tests/examples_tests.cpp | 2 +
src/tests/low_level_scheduler_pthread_test.sh | 28 ++
5 files changed, 542 insertions(+), 25 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/mesos/blob/049aed3c/src/Makefile.am
----------------------------------------------------------------------
diff --git a/src/Makefile.am b/src/Makefile.am
index a1fe9dc..c216e2f 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -966,6 +966,11 @@ low_level_scheduler_libprocess_SOURCES = examples/low_level_scheduler_libprocess
low_level_scheduler_libprocess_CPPFLAGS = $(MESOS_CPPFLAGS)
low_level_scheduler_libprocess_LDADD = libmesos.la
+check_PROGRAMS += low-level-scheduler-pthread
+low_level_scheduler_pthread_SOURCES = examples/low_level_scheduler_pthread.cpp
+low_level_scheduler_pthread_CPPFLAGS = $(MESOS_CPPFLAGS)
+low_level_scheduler_pthread_LDADD = libmesos.la
+
check_PROGRAMS += test-framework
test_framework_SOURCES = examples/test_framework.cpp
test_framework_CPPFLAGS = $(MESOS_CPPFLAGS)
@@ -1114,6 +1119,7 @@ EXTRA_DIST += examples/python/test_containerizer.py \
dist_check_SCRIPTS += \
tests/balloon_framework_test.sh \
tests/low_level_scheduler_libprocess_test.sh \
+ tests/low_level_scheduler_pthread_test.sh \
tests/test_framework_test.sh \
tests/no_executor_framework_test.sh \
tests/java_exception_test.sh \
http://git-wip-us.apache.org/repos/asf/mesos/blob/049aed3c/src/examples/low_level_scheduler_libprocess.cpp
----------------------------------------------------------------------
diff --git a/src/examples/low_level_scheduler_libprocess.cpp b/src/examples/low_level_scheduler_libprocess.cpp
index 784f25e..83fe815 100644
--- a/src/examples/low_level_scheduler_libprocess.cpp
+++ b/src/examples/low_level_scheduler_libprocess.cpp
@@ -67,10 +67,10 @@ class LowLevelScheduler : public process::Process<LowLevelScheduler>
public:
LowLevelScheduler(const FrameworkInfo& _framework,
const ExecutorInfo& _executor,
- const string& _master)
+ const string& master)
: framework(_framework),
executor(_executor),
- mesos(_master,
+ mesos(master,
process::defer(self(), &Self::connected),
process::defer(self(), &Self::disconnected),
process::defer(self(), &Self::received, lambda::_1)),
@@ -81,11 +81,11 @@ public:
LowLevelScheduler(const FrameworkInfo& _framework,
const ExecutorInfo& _executor,
- const string& _master,
+ const string& master,
const Credential& credential)
: framework(_framework),
executor(_executor),
- mesos(_master,
+ mesos(master,
credential,
process::defer(self(), &Self::connected),
process::defer(self(), &Self::disconnected),
@@ -117,8 +117,7 @@ public:
case Event::REGISTERED: {
cout << endl << "Received a REGISTERED event" << endl;
- framework.mutable_id()->CopyFrom(
- event.registered().framework_id());
+ framework.mutable_id()->CopyFrom(event.registered().framework_id());
state = REGISTERED;
cout << "Framework '" << event.registered().framework_id().value()
@@ -166,7 +165,7 @@ public:
cout << endl << "Received a FAILURE event" << endl;
if (event.failure().has_executor_id()) {
- // Executor failure.
+ // Executor failed.
cout << "Executor '"
<< event.failure().executor_id().value() << "' terminated";
@@ -178,16 +177,14 @@ public:
if (event.failure().has_status()) {
int status = event.failure().status();
if (WIFEXITED(status)) {
- cout << ", and exited with status "
- << WEXITSTATUS(status);
+ cout << ", and exited with status " << WEXITSTATUS(status);
} else {
- cout << ", and terminated with signal "
- << WTERMSIG(status);
+ cout << ", and terminated with signal " << WTERMSIG(status);
}
}
cout << endl;
} else if (event.failure().has_slave_id()) {
- // Slave failure.
+ // Slave failed.
cout << "Slave '" << event.failure().slave_id().value()
<< "' terminated" << endl;
}
@@ -195,8 +192,8 @@ public:
}
case Event::ERROR: {
- cout << endl << "Received an ERROR event" << endl;
- cout << event.error().message() << endl;
+ cout << endl << "Received an ERROR event: "
+ << event.error().message() << endl;
process::terminate(self());
break;
}
@@ -299,12 +296,8 @@ private:
Call call;
call.mutable_framework_info()->CopyFrom(framework);
-
- if (state == INITIALIZING) {
- call.set_type(Call::REGISTER);
- } else { // state is DISCONNECTED.
- call.set_type(Call::REREGISTER);
- }
+ call.set_type(
+ state == INITIALIZING ? Call::REGISTER : Call::REREGISTER);
mesos.send(call);
@@ -337,6 +330,7 @@ private:
const int totalTasks;
};
+
void usage(const char* argv0, const flags::FlagsBase& flags)
{
cerr << "Usage: " << os::basename(argv0).get() << " [...]" << endl
@@ -345,13 +339,14 @@ void usage(const char* argv0, const flags::FlagsBase& flags)
<< flags.usage();
}
+
int main(int argc, char** argv)
{
// Find this executable's directory to locate executor.
- string path = os::realpath(dirname(argv[0])).get();
- string uri = path + "/test-executor";
+ string uri =
+ path::join(os::realpath(dirname(argv[0])).get(), "src", "test-executor");
if (getenv("MESOS_BUILD_DIR")) {
- uri = string(getenv("MESOS_BUILD_DIR")) + "/src/test-executor";
+ uri = path::join(os::getenv("MESOS_BUILD_DIR"), "src", "test-executor");
}
mesos::internal::logging::Flags flags;
@@ -372,11 +367,11 @@ int main(int argc, char** argv)
if (load.isError()) {
cerr << load.error() << endl;
usage(argv[0], flags);
- exit(1);
+ EXIT(1);
} else if (master.isNone()) {
cerr << "Missing --master" << endl;
usage(argv[0], flags);
- exit(1);
+ EXIT(1);
}
FrameworkInfo framework;
http://git-wip-us.apache.org/repos/asf/mesos/blob/049aed3c/src/examples/low_level_scheduler_pthread.cpp
----------------------------------------------------------------------
diff --git a/src/examples/low_level_scheduler_pthread.cpp b/src/examples/low_level_scheduler_pthread.cpp
new file mode 100644
index 0000000..1502247
--- /dev/null
+++ b/src/examples/low_level_scheduler_pthread.cpp
@@ -0,0 +1,486 @@
+/**
+ * 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 <libgen.h>
+#include <pthread.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <string>
+#include <queue>
+
+#include <boost/lexical_cast.hpp>
+
+#include <process/protobuf.hpp>
+
+#include <mesos/resources.hpp>
+#include <mesos/scheduler.hpp>
+
+#include <stout/check.hpp>
+#include <stout/exit.hpp>
+#include <stout/flags.hpp>
+#include <stout/foreach.hpp>
+#include <stout/lambda.hpp>
+#include <stout/none.hpp>
+#include <stout/option.hpp>
+#include <stout/os.hpp>
+#include <stout/stringify.hpp>
+
+#include "logging/flags.hpp"
+
+using namespace mesos;
+
+using std::cerr;
+using std::cout;
+using std::endl;
+using std::queue;
+using std::string;
+using std::vector;
+
+using boost::lexical_cast;
+
+using mesos::Resources;
+using mesos::scheduler::Call;
+using mesos::scheduler::Event;
+
+const int32_t CPUS_PER_TASK = 1;
+const int32_t MEM_PER_TASK = 32;
+
+class LowLevelScheduler
+{
+public:
+ LowLevelScheduler(const FrameworkInfo& _framework,
+ const ExecutorInfo& _executor,
+ const string& master)
+ : framework(_framework),
+ executor(_executor),
+ mesos(master,
+ lambda::bind(&LowLevelScheduler::connected, this),
+ lambda::bind(&LowLevelScheduler::disconnected, this),
+ lambda::bind(&LowLevelScheduler::received, this, lambda::_1)),
+ state(INITIALIZING),
+ tasksLaunched(0),
+ tasksFinished(0),
+ totalTasks(5)
+ {
+ pthread_mutex_init(&mutex, NULL);
+ pthread_cond_init(&cond, 0);
+ }
+
+ LowLevelScheduler(const FrameworkInfo& _framework,
+ const ExecutorInfo& _executor,
+ const string& master,
+ const Credential& credential)
+ : framework(_framework),
+ executor(_executor),
+ mesos(master,
+ credential,
+ lambda::bind(&LowLevelScheduler::connected, this),
+ lambda::bind(&LowLevelScheduler::disconnected, this),
+ lambda::bind(&LowLevelScheduler::received, this, lambda::_1)),
+ state(INITIALIZING),
+ tasksLaunched(0),
+ tasksFinished(0),
+ totalTasks(5)
+ {
+ pthread_mutex_init(&mutex, NULL);
+ pthread_cond_init(&cond, 0);
+ }
+
+ ~LowLevelScheduler()
+ {
+ pthread_mutex_destroy(&mutex);
+ pthread_cond_destroy(&cond);
+ }
+
+ void connected(void)
+ {
+ pthread_mutex_lock(&mutex);
+ state = CONNECTED;
+
+ // Unblock the main thread calling wait().
+ pthread_cond_signal(&cond);
+ pthread_mutex_unlock(&mutex);
+ }
+
+ void disconnected(void)
+ {
+ pthread_mutex_lock(&mutex);
+ state = DISCONNECTED;
+ pthread_mutex_unlock(&mutex);
+ }
+
+ void received(queue<Event> events)
+ {
+ while (!events.empty()) {
+ Event event = events.front();
+ events.pop();
+
+ switch (event.type()) {
+ case Event::REGISTERED: {
+ cout << endl << "Received a REGISTERED event" << endl;
+
+ pthread_mutex_lock(&mutex);
+ state = REGISTERED;
+ pthread_mutex_unlock(&mutex);
+
+ framework.mutable_id()->CopyFrom(event.registered().framework_id());
+
+ cout << "Framework '" << event.registered().framework_id().value()
+ << "' registered with Master '"
+ << event.registered().master_info().id() << "'" << endl;
+ break;
+ }
+
+ case Event::REREGISTERED: {
+ cout << endl << "Received a REREGISTERED event" << endl;
+
+ pthread_mutex_lock(&mutex);
+ state = REGISTERED;
+ pthread_mutex_unlock(&mutex);
+
+ cout << "Framework '" << event.reregistered().framework_id().value()
+ << "' re-registered with Master '"
+ << event.reregistered().master_info().id() << "'" << endl;
+ break;
+ }
+
+ case Event::OFFERS: {
+ cout << endl << "Received an OFFERS event" << endl;
+ resourceOffers(google::protobuf::convert(event.offers().offers()));
+ break;
+ }
+
+ case Event::RESCIND: {
+ cout << endl << "Received a RESCIND event" << endl;
+ break;
+ }
+
+ case Event::UPDATE: {
+ cout << endl << "Received an UPDATE event" << endl;
+
+ // TODO(zuyu): Do batch processing of UPDATE events.
+ statusUpdate(event.update().uuid(), event.update().status());
+ break;
+ }
+
+ case Event::MESSAGE: {
+ cout << endl << "Received a MESSAGE event" << endl;
+ break;
+ }
+
+ case Event::FAILURE: {
+ cout << endl << "Received a FAILURE event" << endl;
+
+ if (event.failure().has_executor_id()) {
+ // Executor failed.
+ cout << "Executor '"
+ << event.failure().executor_id().value() << "' terminated";
+
+ if (event.failure().has_slave_id()) {
+ cout << " on Slave '"
+ << event.failure().slave_id().value() << "'";
+ }
+
+ if (event.failure().has_status()) {
+ int status = event.failure().status();
+ if (WIFEXITED(status)) {
+ cout << ", and exited with status " << WEXITSTATUS(status);
+ } else {
+ cout << ", and terminated with signal " << WTERMSIG(status);
+ }
+ }
+ cout << endl;
+ } else {
+ // Slave failed.
+ cout << "Slave '" << event.failure().slave_id().value()
+ << "' terminated" << endl;
+ }
+ break;
+ }
+
+ case Event::ERROR: {
+ cout << endl << "Received an ERROR event: "
+ << event.error().message() << endl;
+ finalize();
+ break;
+ }
+
+ default: {
+ EXIT(1) << "Received an UNKNOWN event";
+ }
+ }
+ }
+ }
+
+ void wait(void)
+ {
+ pthread_mutex_lock(&mutex);
+ if (state == INITIALIZING) {
+ // wait for connected() to be called.
+ pthread_cond_wait(&cond, &mutex);
+ }
+
+ // CONNECTED state.
+ pthread_mutex_unlock(&mutex);
+
+ while (true) {
+ pthread_mutex_lock(&mutex);
+ if (state == CONNECTED ||
+ state == DISCONNECTED) {
+ pthread_mutex_unlock(&mutex);
+ doRegistration();
+ sleep(1);
+ } else if (state == DONE) {
+ pthread_mutex_unlock(&mutex);
+ break;
+ } else {
+ pthread_cond_wait(&cond, &mutex);
+ pthread_mutex_unlock(&mutex);
+ }
+ }
+ }
+
+private:
+ void resourceOffers(const vector<Offer>& offers)
+ {
+ foreach (const Offer& offer, offers) {
+ cout << "Offer '" << offer.id().value() << "' has "
+ << offer.resources() << endl;
+
+ static const Resources TASK_RESOURCES = Resources::parse(
+ "cpus:" + stringify(CPUS_PER_TASK) +
+ ";mem:" + stringify(MEM_PER_TASK)).get();
+
+ Resources remaining = offer.resources();
+
+ // Launch tasks.
+ vector<TaskInfo> tasks;
+ while (tasksLaunched < totalTasks &&
+ TASK_RESOURCES <= remaining.flatten()) {
+ int taskId = tasksLaunched++;
+
+ cout << "Starting task " << taskId << " on "
+ << offer.hostname() << endl;
+
+ TaskInfo task;
+ task.set_name("Task " + lexical_cast<string>(taskId));
+ task.mutable_task_id()->set_value(lexical_cast<string>(taskId));
+ task.mutable_slave_id()->MergeFrom(offer.slave_id());
+ task.mutable_executor()->MergeFrom(executor);
+
+ Option<Resources> resources =
+ remaining.find(TASK_RESOURCES, framework.role());
+
+ CHECK_SOME(resources);
+ task.mutable_resources()->MergeFrom(resources.get());
+ remaining -= resources.get();
+
+ tasks.push_back(task);
+ }
+
+ Call call;
+ call.set_type(Call::LAUNCH);
+ call.mutable_framework_info()->CopyFrom(framework);
+
+ Call::Launch* launch = call.mutable_launch();
+ foreach (const TaskInfo& taskInfo, tasks) {
+ launch->add_task_infos()->CopyFrom(taskInfo);
+ }
+ launch->add_offer_ids()->CopyFrom(offer.id());
+
+ mesos.send(call);
+ }
+ }
+
+ void statusUpdate(const string& uuid, const TaskStatus& status)
+ {
+ cout << "Task " << status.task_id().value() << " is in state "
+ << TaskState_Name(status.state());
+
+ if (status.has_message()) {
+ cout << " with message '" << status.message() << "'";
+ }
+ cout << endl;
+
+ Call call;
+ call.set_type(Call::ACKNOWLEDGE);
+ call.mutable_framework_info()->CopyFrom(framework);
+
+ Call::Acknowledge* ack = call.mutable_acknowledge();
+ ack->mutable_slave_id()->CopyFrom(status.slave_id());
+ ack->mutable_task_id ()->CopyFrom(status.task_id ());
+ ack->set_uuid(uuid);
+
+ mesos.send(call);
+
+ if (status.state() == TASK_FINISHED) {
+ ++tasksFinished;
+ }
+
+ if (tasksFinished == totalTasks) {
+ finalize();
+ }
+ }
+
+ void doRegistration()
+ {
+ pthread_mutex_lock(&mutex);
+ if (state != CONNECTED &&
+ state != DISCONNECTED) {
+ pthread_mutex_unlock(&mutex);
+ return;
+ }
+
+ Call call;
+ call.mutable_framework_info()->CopyFrom(framework);
+ call.set_type(
+ state == CONNECTED ? Call::REGISTER : Call::REREGISTER);
+ pthread_mutex_unlock(&mutex);
+
+ mesos.send(call);
+ }
+
+ void finalize(void)
+ {
+ Call call;
+ call.set_type(Call::UNREGISTER);
+ call.mutable_framework_info()->CopyFrom(framework);
+
+ mesos.send(call);
+
+ pthread_mutex_lock(&mutex);
+ state = DONE;
+ pthread_cond_signal(&cond);
+ pthread_mutex_unlock(&mutex);
+ }
+
+ FrameworkInfo framework;
+ const ExecutorInfo executor;
+ scheduler::Mesos mesos;
+
+ enum State {
+ INITIALIZING = 0,
+ CONNECTED = 1,
+ REGISTERED = 2,
+ DISCONNECTED = 3,
+ DONE = 4
+ } state;
+
+ int tasksLaunched;
+ int tasksFinished;
+ const int totalTasks;
+
+ pthread_mutex_t mutex;
+ pthread_cond_t cond;
+};
+
+
+void usage(const char* argv0, const flags::FlagsBase& flags)
+{
+ cerr << "Usage: " << os::basename(argv0).get() << " [...]" << endl
+ << endl
+ << "Supported options:" << endl
+ << flags.usage();
+}
+
+
+int main(int argc, char** argv)
+{
+ // Find this executable's directory to locate executor.
+ string uri =
+ path::join(os::realpath(dirname(argv[0])).get(), "src", "test-executor");
+ if (getenv("MESOS_BUILD_DIR")) {
+ uri = path::join(os::getenv("MESOS_BUILD_DIR"), "src", "test-executor");
+ }
+
+ mesos::internal::logging::Flags flags;
+
+ string role;
+ flags.add(&role,
+ "role",
+ "Role to use when registering",
+ "*");
+
+ Option<string> master;
+ flags.add(&master,
+ "master",
+ "ip:port of master to connect");
+
+ Try<Nothing> load = flags.load(None(), argc, argv);
+
+ if (load.isError()) {
+ cerr << load.error() << endl;
+ usage(argv[0], flags);
+ EXIT(1);
+ } else if (master.isNone()) {
+ cerr << "Missing --master" << endl;
+ usage(argv[0], flags);
+ EXIT(1);
+ }
+
+ FrameworkInfo framework;
+ framework.set_user(""); // Have Mesos fill in the current user.
+ framework.set_name("Low-Level Scheduler using pthread (C++)");
+ framework.set_role(role);
+
+ // TODO(vinod): Make checkpointing the default when it is default
+ // on the slave.
+ if (os::hasenv("MESOS_CHECKPOINT")) {
+ cout << "Enabling checkpoint for the framework" << endl;
+ framework.set_checkpoint(true);
+ }
+
+ ExecutorInfo executor;
+ executor.mutable_executor_id()->set_value("default");
+ executor.mutable_command()->set_value(uri);
+ executor.set_name("Test Executor (C++)");
+ executor.set_source("cpp_test");
+
+ LowLevelScheduler* scheduler;
+ if (os::hasenv("MESOS_AUTHENTICATE")) {
+ cout << "Enabling authentication for the scheduler" << endl;
+
+ if (!os::hasenv("DEFAULT_PRINCIPAL")) {
+ EXIT(1) << "Expecting authentication principal in the environment";
+ }
+
+ if (!os::hasenv("DEFAULT_SECRET")) {
+ EXIT(1) << "Expecting authentication secret in the environment";
+ }
+
+ Credential credential;
+ credential.set_principal(getenv("DEFAULT_PRINCIPAL"));
+ credential.set_secret(getenv("DEFAULT_SECRET"));
+
+ framework.set_principal(getenv("DEFAULT_PRINCIPAL"));
+
+ scheduler =
+ new LowLevelScheduler(framework, executor, master.get(), credential);
+ } else {
+ framework.set_principal("low-level-scheduler-pthread-cpp");
+
+ scheduler =
+ new LowLevelScheduler(framework, executor, master.get());
+ }
+
+ scheduler->wait();
+ delete scheduler;
+
+ return 0;
+}
http://git-wip-us.apache.org/repos/asf/mesos/blob/049aed3c/src/tests/examples_tests.cpp
----------------------------------------------------------------------
diff --git a/src/tests/examples_tests.cpp b/src/tests/examples_tests.cpp
index 2b554d7..4ee0b30 100644
--- a/src/tests/examples_tests.cpp
+++ b/src/tests/examples_tests.cpp
@@ -26,6 +26,8 @@ TEST_SCRIPT(ExamplesTest, TestFramework, "test_framework_test.sh")
TEST_SCRIPT(ExamplesTest, NoExecutorFramework, "no_executor_framework_test.sh")
TEST_SCRIPT(ExamplesTest, LowLevelSchedulerLibprocess,
"low_level_scheduler_libprocess_test.sh")
+TEST_SCRIPT(ExamplesTest, LowLevelSchedulerPthread,
+ "low_level_scheduler_pthread_test.sh")
#ifdef MESOS_HAS_JAVA
TEST_SCRIPT(ExamplesTest, JavaFramework, "java_framework_test.sh")
http://git-wip-us.apache.org/repos/asf/mesos/blob/049aed3c/src/tests/low_level_scheduler_pthread_test.sh
----------------------------------------------------------------------
diff --git a/src/tests/low_level_scheduler_pthread_test.sh b/src/tests/low_level_scheduler_pthread_test.sh
new file mode 100755
index 0000000..839e8eb
--- /dev/null
+++ b/src/tests/low_level_scheduler_pthread_test.sh
@@ -0,0 +1,28 @@
+#!/usr/bin/env bash
+
+# Expecting MESOS_SOURCE_DIR and MESOS_BUILD_DIR to be in environment.
+
+env | grep MESOS_SOURCE_DIR >/dev/null
+
+test $? != 0 && \
+ echo "Failed to find MESOS_SOURCE_DIR in environment" && \
+ exit 1
+
+env | grep MESOS_BUILD_DIR >/dev/null
+
+test $? != 0 && \
+ echo "Failed to find MESOS_BUILD_DIR in environment" && \
+ exit 1
+
+source ${MESOS_SOURCE_DIR}/support/atexit.sh
+
+MESOS_WORK_DIR=`mktemp -d -t mesos-XXXXXX`
+
+atexit "rm -rf ${MESOS_WORK_DIR}"
+export MESOS_WORK_DIR=${MESOS_WORK_DIR}
+
+# Set local Mesos runner to use 3 slaves
+export MESOS_NUM_SLAVES=3
+
+# Check that the C++ low level scheduler executes without crashing (returns 0).
+exec ${MESOS_BUILD_DIR}/src/low-level-scheduler-pthread --master=local