Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Jobserver fifo #2

Open
wants to merge 2 commits into
base: kitware-staged-features
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/build.cc
Original file line number Diff line number Diff line change
Expand Up @@ -474,9 +474,9 @@ struct RealCommandRunner : public CommandRunner {
RealCommandRunner::RealCommandRunner(const BuildConfig& config) : config_(config) {
max_load_average_ = config.max_load_average;
if ((tokens_ = TokenPool::Get()) != NULL) {
if (!tokens_->Setup(config_.parallelism_from_cmdline,
config_.verbosity == BuildConfig::VERBOSE,
max_load_average_)) {
if (!tokens_->SetupClient(config_.parallelism_from_cmdline,
config_.verbosity == BuildConfig::VERBOSE,
max_load_average_)) {
delete tokens_;
tokens_ = NULL;
}
Expand Down
3 changes: 2 additions & 1 deletion src/subprocess_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ struct TestTokenPool : public TokenPool {
void Reserve() {}
void Release() {}
void Clear() {}
bool Setup(bool ignore_unused, bool verbose, double& max_load_average) { return false; }
bool SetupClient(bool ignore_unused, bool verbose,
double& max_load_average) { return false; }

#ifdef _WIN32
bool _token_available;
Expand Down
46 changes: 45 additions & 1 deletion src/tokenpool-gnu-make-posix.cc
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ struct GNUmakeTokenPoolPosix : public GNUmakeTokenPool {
private:
int rfd_;
int wfd_;
bool closeFds_;

struct sigaction old_act_;
bool restore_;
Expand All @@ -48,14 +49,19 @@ struct GNUmakeTokenPoolPosix : public GNUmakeTokenPool {
static void CloseDupRfd(int signum);

bool CheckFd(int fd);
bool CheckFifo(const char* fifo);
bool SetAlarmHandler();
};

GNUmakeTokenPoolPosix::GNUmakeTokenPoolPosix() : rfd_(-1), wfd_(-1), restore_(false) {
GNUmakeTokenPoolPosix::GNUmakeTokenPoolPosix() : rfd_(-1), wfd_(-1), closeFds_(false), restore_(false) {
}

GNUmakeTokenPoolPosix::~GNUmakeTokenPoolPosix() {
Clear();
if (closeFds_) {
close(wfd_);
close(rfd_);
}
if (restore_)
sigaction(SIGALRM, &old_act_, NULL);
}
Expand All @@ -69,6 +75,36 @@ bool GNUmakeTokenPoolPosix::CheckFd(int fd) {
return true;
}

bool GNUmakeTokenPoolPosix::CheckFifo(const char* fifo) {
// remove possible junk from end of fifo name
char *filename = strdup(fifo);
char *end;
if ((end = strchr(filename, ' ')) != NULL) {
*end = '\0';
}

int rfd = open(filename, O_RDONLY);
if (rfd < 0) {
free(filename);
return false;
}

int wfd = open(filename, O_WRONLY);
if (wfd < 0) {
close(rfd);
free(filename);
return false;
}

free(filename);

rfd_ = rfd;
wfd_ = wfd;
closeFds_ = true;

return true;
}

int GNUmakeTokenPoolPosix::dup_rfd_ = -1;

void GNUmakeTokenPoolPosix::CloseDupRfd(int signum) {
Expand All @@ -89,6 +125,13 @@ bool GNUmakeTokenPoolPosix::SetAlarmHandler() {
}

bool GNUmakeTokenPoolPosix::ParseAuth(const char* jobserver) {
// check for jobserver-fifo style
const char* fifo;
if (((fifo = strstr(jobserver, "=fifo:")) != NULL) &&
CheckFifo(fifo + 6))
return SetAlarmHandler();

// check for legacy simple pipe style
int rfd = -1;
int wfd = -1;
if ((sscanf(jobserver, "%*[^=]=%d,%d", &rfd, &wfd) == 2) &&
Expand All @@ -100,6 +143,7 @@ bool GNUmakeTokenPoolPosix::ParseAuth(const char* jobserver) {
return true;
}

// some jobserver style we don't support
return false;
}

Expand Down
6 changes: 3 additions & 3 deletions src/tokenpool-gnu-make.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ GNUmakeTokenPool::GNUmakeTokenPool() : available_(1), used_(0) {
GNUmakeTokenPool::~GNUmakeTokenPool() {
}

bool GNUmakeTokenPool::Setup(bool ignore,
bool verbose,
double& max_load_average) {
bool GNUmakeTokenPool::SetupClient(bool ignore,
bool verbose,
double& max_load_average) {
const char* value = GetEnv("MAKEFLAGS");
if (!value)
return false;
Expand Down
3 changes: 2 additions & 1 deletion src/tokenpool-gnu-make.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ struct GNUmakeTokenPool : public TokenPool {
virtual void Reserve();
virtual void Release();
virtual void Clear();
virtual bool Setup(bool ignore, bool verbose, double& max_load_average);
virtual bool SetupClient(bool ignore, bool verbose,
double& max_load_average);

// platform specific implementation
virtual const char* GetEnv(const char* name) = 0;
Expand Down
3 changes: 2 additions & 1 deletion src/tokenpool.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ struct TokenPool {
virtual void Clear() = 0;

// returns false if token pool setup failed
virtual bool Setup(bool ignore, bool verbose, double& max_load_average) = 0;
virtual bool SetupClient(bool ignore, bool verbose,
double& max_load_average) = 0;

#ifdef _WIN32
virtual void WaitForTokenAvailability(HANDLE ioport) = 0;
Expand Down
88 changes: 82 additions & 6 deletions src/tokenpool_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@
#ifdef _WIN32
#include <windows.h>
#else
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#endif

#include <stdio.h>
Expand All @@ -35,6 +38,8 @@
#define AUTH_FORMAT(tmpl) "foo " tmpl "=%d,%d bar"
#define ENVIRONMENT_CLEAR() unsetenv("MAKEFLAGS")
#define ENVIRONMENT_INIT(v) setenv("MAKEFLAGS", v, true)

#define FIFO_NAME "ninja-test-tokenpool-fifo"
#endif

namespace {
Expand All @@ -60,11 +65,24 @@ struct TokenPoolTest : public testing::Test {
semaphore_name_ = SEMAPHORE_NAME;
if ((semaphore_ = CreateSemaphore(0, 0, 2, SEMAPHORE_NAME)) == NULL)
#else
if (mkfifo(FIFO_NAME, 0600) < 0) {
ASSERT_TRUE(false);
}

if (pipe(fds_) < 0)
#endif
ASSERT_TRUE(false);
}

void GetPool(bool ignore_jobserver) {
if ((tokens_ = TokenPool::Get()) != NULL) {
if (!tokens_->SetupClient(ignore_jobserver, false, load_avg_)) {
delete tokens_;
tokens_ = NULL;
}
}
}

void CreatePool(const char* format, bool ignore_jobserver = false) {
if (format) {
sprintf(buf_, format,
Expand All @@ -76,18 +94,30 @@ struct TokenPoolTest : public testing::Test {
);
ENVIRONMENT_INIT(buf_);
}
if ((tokens_ = TokenPool::Get()) != NULL) {
if (!tokens_->Setup(ignore_jobserver, false, load_avg_)) {
delete tokens_;
tokens_ = NULL;
}
}
GetPool(ignore_jobserver);
}

void CreateDefaultPool() {
CreatePool(AUTH_FORMAT("--jobserver-auth"));
}

#ifndef _WIN32
void CreateFifoPool() {
// close simple pipe fds...
close(fds_[0]);
close(fds_[1]);

// ... and open named pipe instead
if ((fds_[0] = open(FIFO_NAME, O_RDONLY|O_NONBLOCK)) < 0)
ASSERT_TRUE(false);
if ((fds_[1] = open(FIFO_NAME, O_WRONLY)) < 0)
ASSERT_TRUE(false);

ENVIRONMENT_INIT("foo --jobserver-auth=fifo:" FIFO_NAME " bar");
GetPool(false);
}
#endif

virtual void TearDown() {
if (tokens_)
delete tokens_;
Expand All @@ -96,6 +126,7 @@ struct TokenPoolTest : public testing::Test {
#else
close(fds_[0]);
close(fds_[1]);
unlink(FIFO_NAME);
#endif
ENVIRONMENT_CLEAR();
}
Expand Down Expand Up @@ -167,6 +198,15 @@ TEST_F(TokenPoolTest, MonitorFD) {

EXPECT_EQ(fds_[0], tokens_->GetMonitorFd());
}

TEST_F(TokenPoolTest, MonitorFDFifo) {
CreateFifoPool();

ASSERT_NE(NULL, tokens_);
EXPECT_EQ(kLoadAverageDefault, load_avg_);

EXPECT_NE(-1, tokens_->GetMonitorFd());
}
#endif

TEST_F(TokenPoolTest, ImplicitToken) {
Expand Down Expand Up @@ -226,6 +266,42 @@ TEST_F(TokenPoolTest, TwoTokens) {
EXPECT_TRUE(tokens_->Acquire());
}

#ifndef _WIN32
TEST_F(TokenPoolTest, TwoTokensFifo) {
CreateFifoPool();

ASSERT_NE(NULL, tokens_);
EXPECT_EQ(kLoadAverageDefault, load_avg_);

// implicit token
EXPECT_TRUE(tokens_->Acquire());
tokens_->Reserve();
EXPECT_FALSE(tokens_->Acquire());

// jobserver offers 2nd token
char test_tokens[1] = { '+' };
ASSERT_EQ(1u, write(fds_[1], test_tokens, sizeof(test_tokens)));
EXPECT_TRUE(tokens_->Acquire());
tokens_->Reserve();
EXPECT_FALSE(tokens_->Acquire());

// release 2nd token
tokens_->Release();
EXPECT_TRUE(tokens_->Acquire());

// release implicit token - must return 2nd token back to jobserver
tokens_->Release();
EXPECT_TRUE(tokens_->Acquire());

// there must be one token available
EXPECT_EQ(1u, read(fds_[0], buf_, sizeof(buf_)));
EXPECT_EQ(test_tokens[0], buf_[0]);

// implicit token
EXPECT_TRUE(tokens_->Acquire());
}
#endif

TEST_F(TokenPoolTest, Clear) {
CreateDefaultPool();

Expand Down