summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAmi Fischman <fischman@chromium.org>2012-03-04 01:42:39 (GMT)
committerAmi Fischman <fischman@chromium.org>2012-03-04 01:52:22 (GMT)
commitcd2e3c9115bd2df3db0d11cfc38ab82a17fcac06 (patch)
tree1b3a021d26fbc902954e67e693fd5584598dc275
parent2338d9340bb2c13746805b4b878c889008850962 (diff)
downloadNinja-cd2e3c9115bd2df3db0d11cfc38ab82a17fcac06.zip
Ninja-cd2e3c9115bd2df3db0d11cfc38ab82a17fcac06.tar.gz
Ninja-cd2e3c9115bd2df3db0d11cfc38ab82a17fcac06.tar.bz2
pselect->ppoll on linux to raise the process limit roof
-rwxr-xr-xconfigure.py4
-rw-r--r--src/subprocess.cc60
-rw-r--r--src/subprocess.h4
-rw-r--r--src/subprocess_test.cc22
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