resolves #30: job execution timeout

Add the ability to configure a timeout in seconds
after which a job run will be automatically aborted
pull/53/head
Oliver Giles 6 years ago
parent ce81be85c7
commit 649caee297

@ -361,6 +361,14 @@ make -j4
Laminar will automatically create the workspace for a job if it doesn't exist when a job is executed. In this case, the `/var/lib/laminar/cfg/jobs/JOBNAME.init` will be executed if it exists. This is an excellent place to prepare the workspace to a state where subsequent builds can rely on its content.
### Abort on timeout
To configure a maximum execution time in seconds for a job, add a line to `/var/lib/laminar/cfg/jobs/JOBNAME.conf`:
```
TIMEOUT=120
```
### Nodes and Tags
In Laminar, a *node* is an abstract concept allowing more fine-grained control over job execution scheduling. Each node can be defined to support an integer number of *executors*, which defines how many runs can be executed simultaneously.

@ -1,5 +1,5 @@
///
/// Copyright 2015-2017 Oliver Giles
/// Copyright 2015-2018 Oliver Giles
///
/// This file is part of Laminar
///
@ -655,6 +655,20 @@ void Laminar::assignNewJobs() {
if(fs::exists(cfgDir/"jobs"/run->name+".env"))
run->addEnv((cfgDir/"jobs"/run->name+".env").string());
// add job timeout if specified
if(fs::exists(cfgDir/"jobs"/run->name+".conf")) {
int timeout = parseConfFile(fs::path(cfgDir/"jobs"/run->name+".conf").string().c_str()).get<int>("TIMEOUT", 0);
if(timeout > 0) {
// A raw pointer to run is used here so as not to have a circular reference.
// The captured raw pointer is safe because if the Run is destroyed the Promise
// will be cancelled and the callback never called.
Run* r = run.get();
r->timeout = srv->addTimeout(timeout, [r](){
r->abort();
});
}
}
// start the job
node->busyExecutors++;
run->node = node;

@ -26,6 +26,7 @@
#include <ostream>
#include <unordered_map>
#include <memory>
#include <kj/async.h>
enum class RunState {
UNKNOWN,
@ -90,6 +91,7 @@ public:
pid_t pid;
int fd;
std::unordered_map<std::string, std::string> params;
kj::Promise<void> timeout = kj::NEVER_DONE;
time_t queuedAt;
time_t startedAt;

@ -1,5 +1,5 @@
///
/// Copyright 2015-2017 Oliver Giles
/// Copyright 2015-2018 Oliver Giles
///
/// This file is part of Laminar
///
@ -469,6 +469,12 @@ void Server::addDescriptor(int fd, std::function<void(const char*,size_t)> cb) {
childTasks.add(handleFdRead(event, buffer.asPtr().begin(), cb).attach(std::move(event)).attach(std::move(buffer)));
}
kj::Promise<void> Server::addTimeout(int seconds, std::function<void ()> cb) {
return ioContext.lowLevelProvider->getTimer().afterDelay(seconds * kj::SECONDS).then([cb](){
cb();
}).eagerlyEvaluate(nullptr);
}
void Server::addWatchPath(const char* dpath) {
inotify_add_watch(inotify_fd, dpath, IN_ONLYDIR | IN_CLOSE_WRITE | IN_CREATE | IN_DELETE);
}

@ -43,6 +43,9 @@ public:
// invoked with the read data
void addDescriptor(int fd, std::function<void(const char*,size_t)> cb);
// add a one-shot timer callback
kj::Promise<void> addTimeout(int seconds, std::function<void()> cb);
// add a path to be watched for changes
void addWatchPath(const char* dpath);

@ -24,6 +24,8 @@
class RunTest : public ::testing::Test {
protected:
virtual ~RunTest() noexcept override {}
void SetUp() override {
run.node = node;
}

Loading…
Cancel
Save