2017-07-26 19:13:47 +00:00
|
|
|
/*
|
|
|
|
* Copyright 2017 Google Inc.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
|
|
* found in the LICENSE file.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "ok.h"
|
2017-09-01 13:13:01 +00:00
|
|
|
#include <stdlib.h>
|
2017-07-26 19:13:47 +00:00
|
|
|
|
|
|
|
struct SerialEngine : Engine {
|
|
|
|
static std::unique_ptr<Engine> Factory(Options) {
|
|
|
|
SerialEngine engine;
|
|
|
|
return move_unique(engine);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool crashproof() override { return false; }
|
|
|
|
|
|
|
|
std::future<Status> spawn(std::function<Status(void)> fn) override {
|
|
|
|
return std::async(std::launch::deferred, fn);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
static Register serial("serial",
|
|
|
|
"Run tasks serially on the main thread of a single process.",
|
|
|
|
SerialEngine::Factory);
|
|
|
|
|
|
|
|
struct ThreadEngine : Engine {
|
|
|
|
static std::unique_ptr<Engine> Factory(Options) {
|
|
|
|
ThreadEngine engine;
|
|
|
|
return move_unique(engine);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool crashproof() override { return false; }
|
|
|
|
|
|
|
|
std::future<Status> spawn(std::function<Status(void)> fn) override {
|
|
|
|
return std::async(std::launch::async, fn);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
static Register thread("thread",
|
|
|
|
"Run each task on its own thread of a single process.",
|
|
|
|
ThreadEngine::Factory);
|
|
|
|
|
|
|
|
#if !defined(_MSC_VER)
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
struct ForkEngine : Engine {
|
2017-09-01 13:13:01 +00:00
|
|
|
int limit; // How many concurrent subprocesses do we allow to run at max?
|
|
|
|
int alive = 0; // How many concurrent subprocesses do we have running right now?
|
|
|
|
|
|
|
|
static std::unique_ptr<Engine> Factory(Options options) {
|
2017-07-26 19:13:47 +00:00
|
|
|
ForkEngine engine;
|
2017-09-01 13:13:01 +00:00
|
|
|
engine.limit = atoi(options("limit", "0").c_str());
|
|
|
|
if (engine.limit < 1) {
|
|
|
|
engine.limit = std::thread::hardware_concurrency();
|
|
|
|
}
|
2017-07-26 19:13:47 +00:00
|
|
|
return move_unique(engine);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool crashproof() override { return true; }
|
|
|
|
|
|
|
|
std::future<Status> spawn(std::function<Status(void)> fn) override {
|
2017-09-01 13:13:01 +00:00
|
|
|
if (alive == limit) {
|
|
|
|
// The caller will wait for a child process to finish then try again.
|
|
|
|
return std::future<Status>();
|
|
|
|
}
|
|
|
|
|
2017-07-26 19:13:47 +00:00
|
|
|
switch (fork()) {
|
|
|
|
case 0:
|
|
|
|
// We are the spawned child process.
|
|
|
|
// Run fn() and exit() with its Status as our return code.
|
|
|
|
_exit((int)fn());
|
|
|
|
|
|
|
|
case -1:
|
|
|
|
// The OS won't let us fork() another process right now.
|
|
|
|
// We'll need to wait for at least one live task to finish and try again.
|
|
|
|
return std::future<Status>();
|
|
|
|
|
|
|
|
default:
|
|
|
|
// We succesfully spawned a child process!
|
|
|
|
// This will wait for any spawned process to finish and return its Status.
|
2017-09-01 13:13:01 +00:00
|
|
|
alive++;
|
|
|
|
return std::async(std::launch::deferred, [&] {
|
2017-07-26 19:13:47 +00:00
|
|
|
do {
|
|
|
|
int status;
|
|
|
|
if (wait(&status) > 0) {
|
2017-09-01 13:13:01 +00:00
|
|
|
alive--;
|
2017-07-26 19:13:47 +00:00
|
|
|
return WIFEXITED(status) ? (Status)WEXITSTATUS(status)
|
|
|
|
: Status::Crashed;
|
|
|
|
}
|
|
|
|
} while (errno == EINTR);
|
|
|
|
return Status::None;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
static Register _fork("fork",
|
2017-09-01 13:13:01 +00:00
|
|
|
"Run each task in an independent process with fork(), limit=ncpus.",
|
2017-07-26 19:13:47 +00:00
|
|
|
ForkEngine::Factory);
|
|
|
|
#endif
|