diff options
author | Ami Fischman <fischman@chromium.org> | 2012-03-04 01:42:39 (GMT) |
---|---|---|
committer | Ami Fischman <fischman@chromium.org> | 2012-03-04 01:52:22 (GMT) |
commit | cd2e3c9115bd2df3db0d11cfc38ab82a17fcac06 (patch) | |
tree | 1b3a021d26fbc902954e67e693fd5584598dc275 | |
parent | 2338d9340bb2c13746805b4b878c889008850962 (diff) | |
download | Ninja-cd2e3c9115bd2df3db0d11cfc38ab82a17fcac06.zip Ninja-cd2e3c9115bd2df3db0d11cfc38ab82a17fcac06.tar.gz Ninja-cd2e3c9115bd2df3db0d11cfc38ab82a17fcac06.tar.bz2 |
pselect->ppoll on linux to raise the process limit roof
-rwxr-xr-x | configure.py | 4 | ||||
-rw-r--r-- | src/subprocess.cc | 60 | ||||
-rw-r--r-- | src/subprocess.h | 4 | ||||
-rw-r--r-- | src/subprocess_test.cc | 22 |
4 files changed, 82 insertions, 8 deletions
diff --git a/configure.py b/configure.py index ca8ba74..6ea53c1 100755 --- a/configure.py +++ b/configure.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # -# Copyright 2001 Google Inc. All Rights Reserved. +# Copyright 2012 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -117,6 +117,8 @@ else: '-fno-exceptions', '-fvisibility=hidden', '-pipe', "'-DNINJA_PYTHON=\"%s\"'" % (options.with_python,)] + if platform == 'linux': + cflags += ['-DOS_LINUX'] if options.debug: cflags += ['-D_GLIBCXX_DEBUG', '-D_GLIBCXX_DEBUG_PEDANTIC'] else: diff --git a/src/subprocess.cc b/src/subprocess.cc index d4a7d03..3de574b 100644 --- a/src/subprocess.cc +++ b/src/subprocess.cc @@ -1,4 +1,4 @@ -// Copyright 2011 Google Inc. All Rights Reserved. +// Copyright 2012 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -42,10 +42,12 @@ bool Subprocess::Start(SubprocessSet* set, const string& command) { if (pipe(output_pipe) < 0) Fatal("pipe: %s", strerror(errno)); fd_ = output_pipe[0]; - // fd_ may be a member of the pselect set in SubprocessSet::DoWork. Check - // that it falls below the system limit. +#if !defined(OS_LINUX) + // On linux we use ppoll in DoWork(); elsewhere we use pselect and so must + // avoid overly-large FDs. if (fd_ >= FD_SETSIZE) Fatal("pipe: %s", strerror(EMFILE)); +#endif // !OS_LINUX SetCloseOnExec(fd_); pid_ = fork(); @@ -180,6 +182,55 @@ Subprocess *SubprocessSet::Add(const string &command) { return subprocess; } +#ifdef OS_LINUX +bool SubprocessSet::DoWork() { + struct pollfd* fds = new pollfd[running_.size()](); // XXX: scoped_array? + memset(fds, 0, running_.size() * sizeof(struct pollfd)); + nfds_t nfds = 0; + + for (vector<Subprocess*>::iterator i = running_.begin(); + i != running_.end(); ++i) { + int fd = (*i)->fd_; + if (fd < 0) + continue; + fds[nfds].fd = fd; + fds[nfds].events = POLLIN | POLLPRI | POLLRDHUP; + ++nfds; + } + + int ret = ppoll(fds, nfds, NULL, &old_mask_); + if (ret == -1) { + if (errno != EINTR) { + perror("ninja: ppoll"); + return false; + } + bool interrupted = interrupted_; + interrupted_ = false; + return interrupted; + } + + nfds_t cur_nfd = 0; + for (vector<Subprocess*>::iterator i = running_.begin(); + i != running_.end(); ) { + int fd = (*i)->fd_; + if (fd < 0) + continue; + assert(fd == fds[cur_nfd].fd); + if (fds[cur_nfd++].revents) { + (*i)->OnPipeReady(); + if ((*i)->Done()) { + finished_.push(*i); + i = running_.erase(i); + continue; + } + } + ++i; + } + + return false; +} + +#else // OS_LINUX bool SubprocessSet::DoWork() { fd_set set; int nfds = 0; @@ -213,7 +264,7 @@ bool SubprocessSet::DoWork() { (*i)->OnPipeReady(); if ((*i)->Done()) { finished_.push(*i); - running_.erase(i); + i = running_.erase(i); continue; } } @@ -222,6 +273,7 @@ bool SubprocessSet::DoWork() { return false; } +#endif // OS_LINUX Subprocess* SubprocessSet::NextFinished() { if (finished_.empty()) diff --git a/src/subprocess.h b/src/subprocess.h index 8e0d7f1..3294416 100644 --- a/src/subprocess.h +++ b/src/subprocess.h @@ -1,4 +1,4 @@ -// Copyright 2011 Google Inc. All Rights Reserved. +// Copyright 2012 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -68,7 +68,7 @@ struct Subprocess { friend struct SubprocessSet; }; -/// SubprocessSet runs a pselect() loop around a set of Subprocesses. +/// SubprocessSet runs a ppoll/pselect() loop around a set of Subprocesses. /// DoWork() waits for any state change in subprocesses; finished_ /// is a queue of subprocesses as they finish. struct SubprocessSet { diff --git a/src/subprocess_test.cc b/src/subprocess_test.cc index 5b3e8a3..d7ecb33 100644 --- a/src/subprocess_test.cc +++ b/src/subprocess_test.cc @@ -1,4 +1,4 @@ -// Copyright 2011 Google Inc. All Rights Reserved. +// Copyright 2012 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -142,3 +142,23 @@ TEST_F(SubprocessTest, SetWithMulti) { } } +#ifdef OS_LINUX +TEST_F(SubprocessTest, SetWithLots) { + // Arbitrary big number; needs to be over 1024 to confirm we're no longer + // hostage to pselect. + const size_t kNumProcs = 1025; + vector<Subprocess*> procs; + for (size_t i = 0; i < kNumProcs; ++i) { + Subprocess* subproc = subprocs_.Add("/bin/echo"); + ASSERT_NE((Subprocess *) 0, subproc); + procs.push_back(subproc); + } + while (!subprocs_.running_.empty()) + subprocs_.DoWork(); + for (size_t i = 0; i < procs.size(); ++i) { + ASSERT_EQ(ExitSuccess, procs[i]->Finish()); + ASSERT_NE("", procs[i]->GetOutput()); + } + ASSERT_EQ(kNumProcs, subprocs_.finished_.size()); +} +#endif |