1 From 17d13fd7881fd3ce9f9b9d44ce435d6caf4b8f28 Mon Sep 17 00:00:00 2001
2 From: Stefan Becker <stefanb@gpartner-nvidia.com>
3 Date: Tue, 22 Mar 2016 13:48:07 +0200
4 Subject: [PATCH 01/11] Add GNU make jobserver client support
6 - add new TokenPool interface
7 - GNU make implementation for TokenPool parses and verifies the magic
8 information from the MAKEFLAGS environment variable
9 - RealCommandRunner tries to acquire TokenPool
10 * if no token pool is available then there is no change in behaviour
11 - When a token pool is available then RealCommandRunner behaviour
13 * CanRunMore() only returns true if TokenPool::Acquire() returns true
14 * StartCommand() calls TokenPool::Reserve()
15 * WaitForCommand() calls TokenPool::Release()
17 Documentation for GNU make jobserver
19 http://make.mad-scientist.net/papers/jobserver-implementation/
21 Fixes https://github.com/ninja-build/ninja/issues/1139
24 src/build.cc | 63 ++++++++----
26 src/tokenpool-gnu-make.cc | 211 ++++++++++++++++++++++++++++++++++++++
27 src/tokenpool-none.cc | 27 +++++
28 src/tokenpool.h | 26 +++++
29 6 files changed, 310 insertions(+), 22 deletions(-)
30 create mode 100644 src/tokenpool-gnu-make.cc
31 create mode 100644 src/tokenpool-none.cc
32 create mode 100644 src/tokenpool.h
34 diff --git a/configure.py b/configure.py
35 index 43904349a8..db3492c93c 100755
38 @@ -522,6 +522,7 @@ def has_re2c():
39 objs += cxx(name, variables=cxxvariables)
40 if platform.is_windows():
41 for name in ['subprocess-win32',
43 'includes_normalize-win32',
45 'msvc_helper_main-win32']:
46 @@ -531,6 +532,7 @@ def has_re2c():
49 objs += cxx('subprocess-posix')
50 + objs += cxx('tokenpool-gnu-make')
53 if platform.is_msvc():
54 diff --git a/src/build.cc b/src/build.cc
55 index 6f11ed7a3c..fa096eac33 100644
61 #include "subprocess.h"
62 +#include "tokenpool.h"
66 @@ -149,7 +150,7 @@ void Plan::EdgeWanted(const Edge* edge) {
69 Edge* Plan::FindWork() {
73 EdgeSet::iterator e = ready_.begin();
75 @@ -448,8 +449,8 @@ void Plan::Dump() const {
78 struct RealCommandRunner : public CommandRunner {
79 - explicit RealCommandRunner(const BuildConfig& config) : config_(config) {}
80 - virtual ~RealCommandRunner() {}
81 + explicit RealCommandRunner(const BuildConfig& config);
82 + virtual ~RealCommandRunner();
83 virtual bool CanRunMore() const;
84 virtual bool StartCommand(Edge* edge);
85 virtual bool WaitForCommand(Result* result);
86 @@ -458,9 +459,18 @@ struct RealCommandRunner : public CommandRunner {
88 const BuildConfig& config_;
89 SubprocessSet subprocs_;
91 map<const Subprocess*, Edge*> subproc_to_edge_;
94 +RealCommandRunner::RealCommandRunner(const BuildConfig& config) : config_(config) {
95 + tokens_ = TokenPool::Get();
98 +RealCommandRunner::~RealCommandRunner() {
102 vector<Edge*> RealCommandRunner::GetActiveEdges() {
104 for (map<const Subprocess*, Edge*>::iterator e = subproc_to_edge_.begin();
105 @@ -471,14 +481,18 @@ vector<Edge*> RealCommandRunner::GetActiveEdges() {
107 void RealCommandRunner::Abort() {
113 bool RealCommandRunner::CanRunMore() const {
114 size_t subproc_number =
115 subprocs_.running_.size() + subprocs_.finished_.size();
116 return (int)subproc_number < config_.parallelism
117 - && ((subprocs_.running_.empty() || config_.max_load_average <= 0.0f)
118 - || GetLoadAverage() < config_.max_load_average);
119 + && (subprocs_.running_.empty() ||
120 + ((config_.max_load_average <= 0.0f ||
121 + GetLoadAverage() < config_.max_load_average)
122 + && (!tokens_ || tokens_->Acquire())));
125 bool RealCommandRunner::StartCommand(Edge* edge) {
126 @@ -486,6 +500,8 @@ bool RealCommandRunner::StartCommand(Edge* edge) {
127 Subprocess* subproc = subprocs_.Add(command, edge->use_console());
131 + tokens_->Reserve();
132 subproc_to_edge_.insert(make_pair(subproc, edge));
135 @@ -499,6 +515,9 @@ bool RealCommandRunner::WaitForCommand(Result* result) {
140 + tokens_->Release();
142 result->status = subproc->Finish();
143 result->output = subproc->GetOutput();
145 @@ -621,31 +640,31 @@ bool Builder::Build(string* err) {
146 // Second, we attempt to wait for / reap the next finished command.
147 while (plan_.more_to_do()) {
148 // See if we can start any more commands.
149 - if (failures_allowed && command_runner_->CanRunMore()) {
150 - if (Edge* edge = plan_.FindWork()) {
151 - if (edge->GetBindingBool("generator")) {
152 + if (failures_allowed && plan_.more_ready() &&
153 + command_runner_->CanRunMore()) {
154 + Edge* edge = plan_.FindWork();
155 + if (edge->GetBindingBool("generator")) {
156 scan_.build_log()->Close();
159 - if (!StartEdge(edge, err)) {
160 + if (!StartEdge(edge, err)) {
162 + status_->BuildFinished();
166 + if (edge->is_phony()) {
167 + if (!plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, err)) {
169 status_->BuildFinished();
173 - if (edge->is_phony()) {
174 - if (!plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, err)) {
176 - status_->BuildFinished();
180 - ++pending_commands;
183 - // We made some progress; go back to the main loop.
186 + ++pending_commands;
189 + // We made some progress; go back to the main loop.
193 // See if we can reap any finished commands.
194 diff --git a/src/build.h b/src/build.h
195 index d697dfb89e..7dcd111e61 100644
198 @@ -52,6 +52,9 @@ struct Plan {
199 /// Returns true if there's more work to be done.
200 bool more_to_do() const { return wanted_edges_ > 0 && command_edges_ > 0; }
202 + /// Returns true if there's more edges ready to start
203 + bool more_ready() const { return !ready_.empty(); }
205 /// Dumps the current state of the plan.
208 diff --git a/src/tokenpool-gnu-make.cc b/src/tokenpool-gnu-make.cc
210 index 0000000000..a8f9b7139d
212 +++ b/src/tokenpool-gnu-make.cc
214 +// Copyright 2016 Google Inc. All Rights Reserved.
216 +// Licensed under the Apache License, Version 2.0 (the "License");
217 +// you may not use this file except in compliance with the License.
218 +// You may obtain a copy of the License at
220 +// http://www.apache.org/licenses/LICENSE-2.0
222 +// Unless required by applicable law or agreed to in writing, software
223 +// distributed under the License is distributed on an "AS IS" BASIS,
224 +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
225 +// See the License for the specific language governing permissions and
226 +// limitations under the License.
228 +#include "tokenpool.h"
239 +// TokenPool implementation for GNU make jobserver
240 +// (http://make.mad-scientist.net/papers/jobserver-implementation/)
241 +struct GNUmakeTokenPool : public TokenPool {
242 + GNUmakeTokenPool();
243 + virtual ~GNUmakeTokenPool();
245 + virtual bool Acquire();
246 + virtual void Reserve();
247 + virtual void Release();
248 + virtual void Clear();
262 + struct sigaction old_act_;
265 + static int dup_rfd_;
266 + static void CloseDupRfd(int signum);
268 + bool CheckFd(int fd);
269 + bool SetAlarmHandler();
275 +// every instance owns an implicit token -> available_ == 1
276 +GNUmakeTokenPool::GNUmakeTokenPool() : available_(1), used_(0),
277 + rfd_(-1), wfd_(-1), restore_(false) {
280 +GNUmakeTokenPool::~GNUmakeTokenPool() {
283 + sigaction(SIGALRM, &old_act_, NULL);
286 +bool GNUmakeTokenPool::CheckFd(int fd) {
289 + int ret = fcntl(fd, F_GETFD);
295 +int GNUmakeTokenPool::dup_rfd_ = -1;
297 +void GNUmakeTokenPool::CloseDupRfd(int signum) {
302 +bool GNUmakeTokenPool::SetAlarmHandler() {
303 + struct sigaction act;
304 + memset(&act, 0, sizeof(act));
305 + act.sa_handler = CloseDupRfd;
306 + if (sigaction(SIGALRM, &act, &old_act_) < 0) {
307 + perror("sigaction:");
315 +bool GNUmakeTokenPool::Setup() {
316 + const char *value = getenv("MAKEFLAGS");
319 + const char *jobserver = strstr(value, "--jobserver-fds=");
322 + jobserver = strstr(value, "--jobserver-auth=");
326 + if ((sscanf(jobserver, "%*[^=]=%d,%d", &rfd, &wfd) == 2) &&
329 + SetAlarmHandler()) {
330 + printf("ninja: using GNU make jobserver.\n");
341 +bool GNUmakeTokenPool::Acquire() {
342 + if (available_ > 0)
346 + pollfd pollfds[] = {{rfd_, POLLIN, 0}};
347 + int ret = poll(pollfds, 1, 0);
350 + struct timeval timeout = { 0, 0 };
352 + FD_SET(rfd_, &set);
353 + int ret = select(rfd_ + 1, &set, NULL, NULL, &timeout);
356 + dup_rfd_ = dup(rfd_);
358 + if (dup_rfd_ != -1) {
359 + struct sigaction act, old_act;
362 + memset(&act, 0, sizeof(act));
363 + act.sa_handler = CloseDupRfd;
364 + if (sigaction(SIGCHLD, &act, &old_act) == 0) {
367 + // block until token read, child exits or timeout
369 + ret = read(dup_rfd_, &buf, 1);
372 + sigaction(SIGCHLD, &old_act, NULL);
386 +void GNUmakeTokenPool::Reserve() {
391 +void GNUmakeTokenPool::Return() {
392 + const char buf = '+';
394 + int ret = write(wfd_, &buf, 1);
397 + if ((ret != -1) || (errno != EINTR))
399 + // write got interrupted - retry
403 +void GNUmakeTokenPool::Release() {
406 + if (available_ > 1)
410 +void GNUmakeTokenPool::Clear() {
413 + while (available_ > 1)
417 +struct TokenPool *TokenPool::Get(void) {
418 + GNUmakeTokenPool *tokenpool = new GNUmakeTokenPool;
419 + if (tokenpool->Setup())
425 diff --git a/src/tokenpool-none.cc b/src/tokenpool-none.cc
427 index 0000000000..602b3316f5
429 +++ b/src/tokenpool-none.cc
431 +// Copyright 2016 Google Inc. All Rights Reserved.
433 +// Licensed under the Apache License, Version 2.0 (the "License");
434 +// you may not use this file except in compliance with the License.
435 +// You may obtain a copy of the License at
437 +// http://www.apache.org/licenses/LICENSE-2.0
439 +// Unless required by applicable law or agreed to in writing, software
440 +// distributed under the License is distributed on an "AS IS" BASIS,
441 +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
442 +// See the License for the specific language governing permissions and
443 +// limitations under the License.
445 +#include "tokenpool.h"
454 +// No-op TokenPool implementation
455 +struct TokenPool *TokenPool::Get(void) {
458 diff --git a/src/tokenpool.h b/src/tokenpool.h
460 index 0000000000..f560b1083b
462 +++ b/src/tokenpool.h
464 +// Copyright 2016 Google Inc. All Rights Reserved.
466 +// Licensed under the Apache License, Version 2.0 (the "License");
467 +// you may not use this file except in compliance with the License.
468 +// You may obtain a copy of the License at
470 +// http://www.apache.org/licenses/LICENSE-2.0
472 +// Unless required by applicable law or agreed to in writing, software
473 +// distributed under the License is distributed on an "AS IS" BASIS,
474 +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
475 +// See the License for the specific language governing permissions and
476 +// limitations under the License.
478 +// interface to token pool
480 + virtual ~TokenPool() {}
482 + virtual bool Acquire() = 0;
483 + virtual void Reserve() = 0;
484 + virtual void Release() = 0;
485 + virtual void Clear() = 0;
487 + // returns NULL if token pool is not available
488 + static struct TokenPool *Get(void);
491 From ccaccc610cd456f6068758f82e72006364c7380b Mon Sep 17 00:00:00 2001
492 From: Stefan Becker <stefanb@gpartner-nvidia.com>
493 Date: Fri, 27 May 2016 16:47:10 +0300
494 Subject: [PATCH 02/11] Add TokenPool monitoring to SubprocessSet::DoWork()
496 Improve on the original jobserver client implementation. This makes
497 ninja a more aggressive GNU make jobserver client.
499 - add monitor interface to TokenPool
500 - TokenPool is passed down when main loop indicates that more work is
501 ready and would be allowed to start if a token becomes available
502 - posix: update DoWork() to monitor TokenPool read file descriptor
503 - WaitForCommand() exits when DoWork() sets token flag
504 - Main loop starts over when WaitForCommand() sets token exit status
506 src/build.cc | 53 +++++++++++++++++++++++++++++----------
508 src/build_test.cc | 9 +++++--
509 src/exit_status.h | 3 ++-
510 src/subprocess-posix.cc | 33 ++++++++++++++++++++++--
511 src/subprocess-win32.cc | 2 +-
512 src/subprocess.h | 8 +++++-
513 src/subprocess_test.cc | 47 +++++++++++++++++++++++-----------
514 src/tokenpool-gnu-make.cc | 5 ++++
515 src/tokenpool.h | 6 +++++
516 10 files changed, 134 insertions(+), 35 deletions(-)
518 diff --git a/src/build.cc b/src/build.cc
519 index fa096eac33..a25c349050 100644
522 @@ -48,8 +48,9 @@ struct DryRunCommandRunner : public CommandRunner {
524 // Overridden from CommandRunner:
525 virtual bool CanRunMore() const;
526 + virtual bool AcquireToken();
527 virtual bool StartCommand(Edge* edge);
528 - virtual bool WaitForCommand(Result* result);
529 + virtual bool WaitForCommand(Result* result, bool more_ready);
532 queue<Edge*> finished_;
533 @@ -59,12 +60,16 @@ bool DryRunCommandRunner::CanRunMore() const {
537 +bool DryRunCommandRunner::AcquireToken() {
541 bool DryRunCommandRunner::StartCommand(Edge* edge) {
542 finished_.push(edge);
546 -bool DryRunCommandRunner::WaitForCommand(Result* result) {
547 +bool DryRunCommandRunner::WaitForCommand(Result* result, bool more_ready) {
548 if (finished_.empty())
551 @@ -452,8 +457,9 @@ struct RealCommandRunner : public CommandRunner {
552 explicit RealCommandRunner(const BuildConfig& config);
553 virtual ~RealCommandRunner();
554 virtual bool CanRunMore() const;
555 + virtual bool AcquireToken();
556 virtual bool StartCommand(Edge* edge);
557 - virtual bool WaitForCommand(Result* result);
558 + virtual bool WaitForCommand(Result* result, bool more_ready);
559 virtual vector<Edge*> GetActiveEdges();
560 virtual void Abort();
562 @@ -490,9 +496,12 @@ bool RealCommandRunner::CanRunMore() const {
563 subprocs_.running_.size() + subprocs_.finished_.size();
564 return (int)subproc_number < config_.parallelism
565 && (subprocs_.running_.empty() ||
566 - ((config_.max_load_average <= 0.0f ||
567 - GetLoadAverage() < config_.max_load_average)
568 - && (!tokens_ || tokens_->Acquire())));
569 + (config_.max_load_average <= 0.0f ||
570 + GetLoadAverage() < config_.max_load_average));
573 +bool RealCommandRunner::AcquireToken() {
574 + return (!tokens_ || tokens_->Acquire());
577 bool RealCommandRunner::StartCommand(Edge* edge) {
578 @@ -507,14 +516,23 @@ bool RealCommandRunner::StartCommand(Edge* edge) {
582 -bool RealCommandRunner::WaitForCommand(Result* result) {
583 +bool RealCommandRunner::WaitForCommand(Result* result, bool more_ready) {
585 - while ((subproc = subprocs_.NextFinished()) == NULL) {
586 - bool interrupted = subprocs_.DoWork();
587 + subprocs_.ResetTokenAvailable();
588 + while (((subproc = subprocs_.NextFinished()) == NULL) &&
589 + !subprocs_.IsTokenAvailable()) {
590 + bool interrupted = subprocs_.DoWork(more_ready ? tokens_ : NULL);
595 + // token became available
596 + if (subproc == NULL) {
597 + result->status = ExitTokenAvailable;
601 + // command completed
605 @@ -639,9 +657,14 @@ bool Builder::Build(string* err) {
607 // Second, we attempt to wait for / reap the next finished command.
608 while (plan_.more_to_do()) {
609 - // See if we can start any more commands.
610 - if (failures_allowed && plan_.more_ready() &&
611 - command_runner_->CanRunMore()) {
612 + // See if we can start any more commands...
613 + bool can_run_more =
614 + failures_allowed &&
615 + plan_.more_ready() &&
616 + command_runner_->CanRunMore();
618 + // ... but we also need a token to do that.
619 + if (can_run_more && command_runner_->AcquireToken()) {
620 Edge* edge = plan_.FindWork();
621 if (edge->GetBindingBool("generator")) {
622 scan_.build_log()->Close();
623 @@ -670,7 +693,7 @@ bool Builder::Build(string* err) {
624 // See if we can reap any finished commands.
625 if (pending_commands) {
626 CommandRunner::Result result;
627 - if (!command_runner_->WaitForCommand(&result) ||
628 + if (!command_runner_->WaitForCommand(&result, can_run_more) ||
629 result.status == ExitInterrupted) {
631 status_->BuildFinished();
632 @@ -678,6 +701,10 @@ bool Builder::Build(string* err) {
636 + // We might be able to start another command; start the main loop over.
637 + if (result.status == ExitTokenAvailable)
641 if (!FinishCommand(&result, err)) {
643 diff --git a/src/build.h b/src/build.h
644 index 7dcd111e61..35c7b97d12 100644
647 @@ -139,6 +139,7 @@ struct Plan {
648 struct CommandRunner {
649 virtual ~CommandRunner() {}
650 virtual bool CanRunMore() const = 0;
651 + virtual bool AcquireToken() = 0;
652 virtual bool StartCommand(Edge* edge) = 0;
654 /// The result of waiting for a command.
655 @@ -150,7 +151,7 @@ struct CommandRunner {
656 bool success() const { return status == ExitSuccess; }
658 /// Wait for a command to complete, or return false if interrupted.
659 - virtual bool WaitForCommand(Result* result) = 0;
660 + virtual bool WaitForCommand(Result* result, bool more_ready) = 0;
662 virtual std::vector<Edge*> GetActiveEdges() { return std::vector<Edge*>(); }
663 virtual void Abort() {}
664 diff --git a/src/build_test.cc b/src/build_test.cc
665 index 4ef62b2113..7a5ff4015a 100644
666 --- a/src/build_test.cc
667 +++ b/src/build_test.cc
668 @@ -474,8 +474,9 @@ struct FakeCommandRunner : public CommandRunner {
670 // CommandRunner impl
671 virtual bool CanRunMore() const;
672 + virtual bool AcquireToken();
673 virtual bool StartCommand(Edge* edge);
674 - virtual bool WaitForCommand(Result* result);
675 + virtual bool WaitForCommand(Result* result, bool more_ready);
676 virtual vector<Edge*> GetActiveEdges();
677 virtual void Abort();
679 @@ -578,6 +579,10 @@ bool FakeCommandRunner::CanRunMore() const {
680 return active_edges_.size() < max_active_edges_;
683 +bool FakeCommandRunner::AcquireToken() {
687 bool FakeCommandRunner::StartCommand(Edge* edge) {
688 assert(active_edges_.size() < max_active_edges_);
689 assert(find(active_edges_.begin(), active_edges_.end(), edge)
690 @@ -649,7 +654,7 @@ bool FakeCommandRunner::StartCommand(Edge* edge) {
694 -bool FakeCommandRunner::WaitForCommand(Result* result) {
695 +bool FakeCommandRunner::WaitForCommand(Result* result, bool more_ready) {
696 if (active_edges_.empty())
699 diff --git a/src/exit_status.h b/src/exit_status.h
700 index a714ece791..75ebf6a7a0 100644
701 --- a/src/exit_status.h
702 +++ b/src/exit_status.h
708 + ExitTokenAvailable,
712 #endif // NINJA_EXIT_STATUS_H_
713 diff --git a/src/subprocess-posix.cc b/src/subprocess-posix.cc
714 index 8e785406c9..74451b0be2 100644
715 --- a/src/subprocess-posix.cc
716 +++ b/src/subprocess-posix.cc
718 // limitations under the License.
720 #include "subprocess.h"
721 +#include "tokenpool.h"
723 #include <sys/select.h>
725 @@ -249,7 +250,7 @@ Subprocess *SubprocessSet::Add(const string& command, bool use_console) {
729 -bool SubprocessSet::DoWork() {
730 +bool SubprocessSet::DoWork(struct TokenPool* tokens) {
734 @@ -263,6 +264,12 @@ bool SubprocessSet::DoWork() {
739 + pollfd pfd = { tokens->GetMonitorFd(), POLLIN | POLLPRI, 0 };
740 + fds.push_back(pfd);
745 int ret = ppoll(&fds.front(), nfds, NULL, &old_mask_);
747 @@ -295,11 +302,20 @@ bool SubprocessSet::DoWork() {
752 + pollfd *pfd = &fds[nfds - 1];
753 + if (pfd->fd >= 0) {
754 + assert(pfd->fd == tokens->GetMonitorFd());
755 + if (pfd->revents != 0)
756 + token_available_ = true;
760 return IsInterrupted();
763 #else // !defined(USE_PPOLL)
764 -bool SubprocessSet::DoWork() {
765 +bool SubprocessSet::DoWork(struct TokenPool* tokens) {
769 @@ -314,6 +330,13 @@ bool SubprocessSet::DoWork() {
774 + int fd = tokens->GetMonitorFd();
781 int ret = pselect(nfds, &set, 0, 0, 0, &old_mask_);
783 @@ -342,6 +365,12 @@ bool SubprocessSet::DoWork() {
788 + int fd = tokens->GetMonitorFd();
789 + if ((fd >= 0) && FD_ISSET(fd, &set))
790 + token_available_ = true;
793 return IsInterrupted();
795 #endif // !defined(USE_PPOLL)
796 diff --git a/src/subprocess-win32.cc b/src/subprocess-win32.cc
797 index ff3baaca7f..66d2c2c430 100644
798 --- a/src/subprocess-win32.cc
799 +++ b/src/subprocess-win32.cc
800 @@ -251,7 +251,7 @@ Subprocess *SubprocessSet::Add(const string& command, bool use_console) {
804 -bool SubprocessSet::DoWork() {
805 +bool SubprocessSet::DoWork(struct TokenPool* tokens) {
808 OVERLAPPED* overlapped;
809 diff --git a/src/subprocess.h b/src/subprocess.h
810 index 9e3d2ee98f..9ea67ea477 100644
811 --- a/src/subprocess.h
812 +++ b/src/subprocess.h
813 @@ -76,6 +76,8 @@ struct Subprocess {
814 friend struct SubprocessSet;
819 /// SubprocessSet runs a ppoll/pselect() loop around a set of Subprocesses.
820 /// DoWork() waits for any state change in subprocesses; finished_
821 /// is a queue of subprocesses as they finish.
822 @@ -84,13 +86,17 @@ struct SubprocessSet {
825 Subprocess* Add(const std::string& command, bool use_console = false);
827 + bool DoWork(struct TokenPool* tokens);
828 Subprocess* NextFinished();
831 std::vector<Subprocess*> running_;
832 std::queue<Subprocess*> finished_;
834 + bool token_available_;
835 + bool IsTokenAvailable() { return token_available_; }
836 + void ResetTokenAvailable() { token_available_ = false; }
839 static BOOL WINAPI NotifyInterrupted(DWORD dwCtrlType);
840 static HANDLE ioport_;
841 diff --git a/src/subprocess_test.cc b/src/subprocess_test.cc
842 index 073fe86931..4bc8083e26 100644
843 --- a/src/subprocess_test.cc
844 +++ b/src/subprocess_test.cc
845 @@ -45,10 +45,12 @@ TEST_F(SubprocessTest, BadCommandStderr) {
846 Subprocess* subproc = subprocs_.Add("cmd /c ninja_no_such_command");
847 ASSERT_NE((Subprocess *) 0, subproc);
849 + subprocs_.ResetTokenAvailable();
850 while (!subproc->Done()) {
851 // Pretend we discovered that stderr was ready for writing.
852 - subprocs_.DoWork();
853 + subprocs_.DoWork(NULL);
855 + ASSERT_EQ(false, subprocs_.IsTokenAvailable());
857 EXPECT_EQ(ExitFailure, subproc->Finish());
858 EXPECT_NE("", subproc->GetOutput());
859 @@ -59,10 +61,12 @@ TEST_F(SubprocessTest, NoSuchCommand) {
860 Subprocess* subproc = subprocs_.Add("ninja_no_such_command");
861 ASSERT_NE((Subprocess *) 0, subproc);
863 + subprocs_.ResetTokenAvailable();
864 while (!subproc->Done()) {
865 // Pretend we discovered that stderr was ready for writing.
866 - subprocs_.DoWork();
867 + subprocs_.DoWork(NULL);
869 + ASSERT_EQ(false, subprocs_.IsTokenAvailable());
871 EXPECT_EQ(ExitFailure, subproc->Finish());
872 EXPECT_NE("", subproc->GetOutput());
873 @@ -78,9 +82,11 @@ TEST_F(SubprocessTest, InterruptChild) {
874 Subprocess* subproc = subprocs_.Add("kill -INT $$");
875 ASSERT_NE((Subprocess *) 0, subproc);
877 + subprocs_.ResetTokenAvailable();
878 while (!subproc->Done()) {
879 - subprocs_.DoWork();
880 + subprocs_.DoWork(NULL);
882 + ASSERT_EQ(false, subprocs_.IsTokenAvailable());
884 EXPECT_EQ(ExitInterrupted, subproc->Finish());
886 @@ -90,7 +96,7 @@ TEST_F(SubprocessTest, InterruptParent) {
887 ASSERT_NE((Subprocess *) 0, subproc);
889 while (!subproc->Done()) {
890 - bool interrupted = subprocs_.DoWork();
891 + bool interrupted = subprocs_.DoWork(NULL);
895 @@ -102,9 +108,11 @@ TEST_F(SubprocessTest, InterruptChildWithSigTerm) {
896 Subprocess* subproc = subprocs_.Add("kill -TERM $$");
897 ASSERT_NE((Subprocess *) 0, subproc);
899 + subprocs_.ResetTokenAvailable();
900 while (!subproc->Done()) {
901 - subprocs_.DoWork();
902 + subprocs_.DoWork(NULL);
904 + ASSERT_EQ(false, subprocs_.IsTokenAvailable());
906 EXPECT_EQ(ExitInterrupted, subproc->Finish());
908 @@ -114,7 +122,7 @@ TEST_F(SubprocessTest, InterruptParentWithSigTerm) {
909 ASSERT_NE((Subprocess *) 0, subproc);
911 while (!subproc->Done()) {
912 - bool interrupted = subprocs_.DoWork();
913 + bool interrupted = subprocs_.DoWork(NULL);
917 @@ -126,9 +134,11 @@ TEST_F(SubprocessTest, InterruptChildWithSigHup) {
918 Subprocess* subproc = subprocs_.Add("kill -HUP $$");
919 ASSERT_NE((Subprocess *) 0, subproc);
921 + subprocs_.ResetTokenAvailable();
922 while (!subproc->Done()) {
923 - subprocs_.DoWork();
924 + subprocs_.DoWork(NULL);
926 + ASSERT_EQ(false, subprocs_.IsTokenAvailable());
928 EXPECT_EQ(ExitInterrupted, subproc->Finish());
930 @@ -138,7 +148,7 @@ TEST_F(SubprocessTest, InterruptParentWithSigHup) {
931 ASSERT_NE((Subprocess *) 0, subproc);
933 while (!subproc->Done()) {
934 - bool interrupted = subprocs_.DoWork();
935 + bool interrupted = subprocs_.DoWork(NULL);
939 @@ -153,9 +163,11 @@ TEST_F(SubprocessTest, Console) {
940 subprocs_.Add("test -t 0 -a -t 1 -a -t 2", /*use_console=*/true);
941 ASSERT_NE((Subprocess*)0, subproc);
943 + subprocs_.ResetTokenAvailable();
944 while (!subproc->Done()) {
945 - subprocs_.DoWork();
946 + subprocs_.DoWork(NULL);
948 + ASSERT_EQ(false, subprocs_.IsTokenAvailable());
950 EXPECT_EQ(ExitSuccess, subproc->Finish());
952 @@ -167,9 +179,11 @@ TEST_F(SubprocessTest, SetWithSingle) {
953 Subprocess* subproc = subprocs_.Add(kSimpleCommand);
954 ASSERT_NE((Subprocess *) 0, subproc);
956 + subprocs_.ResetTokenAvailable();
957 while (!subproc->Done()) {
958 - subprocs_.DoWork();
959 + subprocs_.DoWork(NULL);
961 + ASSERT_EQ(false, subprocs_.IsTokenAvailable());
962 ASSERT_EQ(ExitSuccess, subproc->Finish());
963 ASSERT_NE("", subproc->GetOutput());
965 @@ -200,12 +214,13 @@ TEST_F(SubprocessTest, SetWithMulti) {
966 ASSERT_EQ("", processes[i]->GetOutput());
969 + subprocs_.ResetTokenAvailable();
970 while (!processes[0]->Done() || !processes[1]->Done() ||
971 !processes[2]->Done()) {
972 ASSERT_GT(subprocs_.running_.size(), 0u);
973 - subprocs_.DoWork();
974 + subprocs_.DoWork(NULL);
977 + ASSERT_EQ(false, subprocs_.IsTokenAvailable());
978 ASSERT_EQ(0u, subprocs_.running_.size());
979 ASSERT_EQ(3u, subprocs_.finished_.size());
981 @@ -237,8 +252,10 @@ TEST_F(SubprocessTest, SetWithLots) {
982 ASSERT_NE((Subprocess *) 0, subproc);
983 procs.push_back(subproc);
985 + subprocs_.ResetTokenAvailable();
986 while (!subprocs_.running_.empty())
987 - subprocs_.DoWork();
988 + subprocs_.DoWork(NULL);
989 + ASSERT_EQ(false, subprocs_.IsTokenAvailable());
990 for (size_t i = 0; i < procs.size(); ++i) {
991 ASSERT_EQ(ExitSuccess, procs[i]->Finish());
992 ASSERT_NE("", procs[i]->GetOutput());
993 @@ -254,9 +271,11 @@ TEST_F(SubprocessTest, SetWithLots) {
994 // that stdin is closed.
995 TEST_F(SubprocessTest, ReadStdin) {
996 Subprocess* subproc = subprocs_.Add("cat -");
997 + subprocs_.ResetTokenAvailable();
998 while (!subproc->Done()) {
999 - subprocs_.DoWork();
1000 + subprocs_.DoWork(NULL);
1002 + ASSERT_EQ(false, subprocs_.IsTokenAvailable());
1003 ASSERT_EQ(ExitSuccess, subproc->Finish());
1004 ASSERT_EQ(1u, subprocs_.finished_.size());
1006 diff --git a/src/tokenpool-gnu-make.cc b/src/tokenpool-gnu-make.cc
1007 index a8f9b7139d..396bb7d874 100644
1008 --- a/src/tokenpool-gnu-make.cc
1009 +++ b/src/tokenpool-gnu-make.cc
1010 @@ -33,6 +33,7 @@ struct GNUmakeTokenPool : public TokenPool {
1011 virtual void Reserve();
1012 virtual void Release();
1013 virtual void Clear();
1014 + virtual int GetMonitorFd();
1018 @@ -201,6 +202,10 @@ void GNUmakeTokenPool::Clear() {
1022 +int GNUmakeTokenPool::GetMonitorFd() {
1026 struct TokenPool *TokenPool::Get(void) {
1027 GNUmakeTokenPool *tokenpool = new GNUmakeTokenPool;
1028 if (tokenpool->Setup())
1029 diff --git a/src/tokenpool.h b/src/tokenpool.h
1030 index f560b1083b..301e1998ee 100644
1031 --- a/src/tokenpool.h
1032 +++ b/src/tokenpool.h
1033 @@ -21,6 +21,12 @@ struct TokenPool {
1034 virtual void Release() = 0;
1035 virtual void Clear() = 0;
1040 + virtual int GetMonitorFd() = 0;
1043 // returns NULL if token pool is not available
1044 static struct TokenPool *Get(void);
1047 From d09f3d77821b3b1fdf09fc0ef8e814907675eafb Mon Sep 17 00:00:00 2001
1048 From: Stefan Becker <chemobejk@gmail.com>
1049 Date: Sun, 12 Nov 2017 16:58:55 +0200
1050 Subject: [PATCH 03/11] Ignore jobserver when -jN is forced on command line
1052 This emulates the behaviour of GNU make.
1054 - add parallelism_from_cmdline flag to build configuration
1055 - set the flag when -jN is given on command line
1056 - pass the flag to TokenPool::Get()
1057 - GNUmakeTokenPool::Setup()
1058 * prints a warning when the flag is true and jobserver was detected
1059 * returns false, i.e. jobserver will be ignored
1060 - ignore config.parallelism in CanRunMore() when we have a valid
1061 TokenPool, because it gets always initialized to a default when not
1062 given on the command line
1064 src/build.cc | 10 ++++++----
1065 src/build.h | 4 +++-
1067 src/tokenpool-gnu-make.cc | 34 +++++++++++++++++++---------------
1068 src/tokenpool-none.cc | 4 ++--
1069 src/tokenpool.h | 4 ++--
1070 6 files changed, 33 insertions(+), 24 deletions(-)
1072 diff --git a/src/build.cc b/src/build.cc
1073 index a25c349050..406a84ec39 100644
1076 @@ -470,7 +470,7 @@ struct RealCommandRunner : public CommandRunner {
1079 RealCommandRunner::RealCommandRunner(const BuildConfig& config) : config_(config) {
1080 - tokens_ = TokenPool::Get();
1081 + tokens_ = TokenPool::Get(config_.parallelism_from_cmdline);
1084 RealCommandRunner::~RealCommandRunner() {
1085 @@ -492,9 +492,11 @@ void RealCommandRunner::Abort() {
1088 bool RealCommandRunner::CanRunMore() const {
1089 - size_t subproc_number =
1090 - subprocs_.running_.size() + subprocs_.finished_.size();
1091 - return (int)subproc_number < config_.parallelism
1092 + bool parallelism_limit_not_reached =
1093 + tokens_ || // ignore config_.parallelism
1094 + ((int) (subprocs_.running_.size() +
1095 + subprocs_.finished_.size()) < config_.parallelism);
1096 + return parallelism_limit_not_reached
1097 && (subprocs_.running_.empty() ||
1098 (config_.max_load_average <= 0.0f ||
1099 GetLoadAverage() < config_.max_load_average));
1100 diff --git a/src/build.h b/src/build.h
1101 index 35c7b97d12..dfde576573 100644
1104 @@ -159,7 +159,8 @@ struct CommandRunner {
1106 /// Options (e.g. verbosity, parallelism) passed to a build.
1107 struct BuildConfig {
1108 - BuildConfig() : verbosity(NORMAL), dry_run(false), parallelism(1),
1109 + BuildConfig() : verbosity(NORMAL), dry_run(false),
1110 + parallelism(1), parallelism_from_cmdline(false),
1111 failures_allowed(1), max_load_average(-0.0f) {}
1114 @@ -171,6 +172,7 @@ struct BuildConfig {
1115 Verbosity verbosity;
1118 + bool parallelism_from_cmdline;
1119 int failures_allowed;
1120 /// The maximum load average we must not exceed. A negative value
1121 /// means that we do not have any limit.
1122 diff --git a/src/ninja.cc b/src/ninja.cc
1123 index df39ba92d1..d904c56c4e 100644
1126 @@ -1447,6 +1447,7 @@ int ReadFlags(int* argc, char*** argv,
1127 // We want to run N jobs in parallel. For N = 0, INT_MAX
1128 // is close enough to infinite for most sane builds.
1129 config->parallelism = value > 0 ? value : INT_MAX;
1130 + config->parallelism_from_cmdline = true;
1131 deferGuessParallelism.needGuess = false;
1134 diff --git a/src/tokenpool-gnu-make.cc b/src/tokenpool-gnu-make.cc
1135 index 396bb7d874..af4be05a31 100644
1136 --- a/src/tokenpool-gnu-make.cc
1137 +++ b/src/tokenpool-gnu-make.cc
1139 -// Copyright 2016 Google Inc. All Rights Reserved.
1140 +// Copyright 2016-2017 Google Inc. All Rights Reserved.
1142 // Licensed under the Apache License, Version 2.0 (the "License");
1143 // you may not use this file except in compliance with the License.
1144 @@ -35,7 +35,7 @@ struct GNUmakeTokenPool : public TokenPool {
1145 virtual void Clear();
1146 virtual int GetMonitorFd();
1149 + bool Setup(bool ignore);
1153 @@ -100,7 +100,7 @@ bool GNUmakeTokenPool::SetAlarmHandler() {
1157 -bool GNUmakeTokenPool::Setup() {
1158 +bool GNUmakeTokenPool::Setup(bool ignore) {
1159 const char *value = getenv("MAKEFLAGS");
1162 @@ -109,16 +109,20 @@ bool GNUmakeTokenPool::Setup() {
1164 jobserver = strstr(value, "--jobserver-auth=");
1168 - if ((sscanf(jobserver, "%*[^=]=%d,%d", &rfd, &wfd) == 2) &&
1171 - SetAlarmHandler()) {
1172 - printf("ninja: using GNU make jobserver.\n");
1177 + printf("ninja: warning: -jN forced on command line; ignoring GNU make jobserver.\n");
1181 + if ((sscanf(jobserver, "%*[^=]=%d,%d", &rfd, &wfd) == 2) &&
1184 + SetAlarmHandler()) {
1185 + printf("ninja: using GNU make jobserver.\n");
1193 @@ -206,9 +210,9 @@ int GNUmakeTokenPool::GetMonitorFd() {
1197 -struct TokenPool *TokenPool::Get(void) {
1198 +struct TokenPool *TokenPool::Get(bool ignore) {
1199 GNUmakeTokenPool *tokenpool = new GNUmakeTokenPool;
1200 - if (tokenpool->Setup())
1201 + if (tokenpool->Setup(ignore))
1205 diff --git a/src/tokenpool-none.cc b/src/tokenpool-none.cc
1206 index 602b3316f5..199b22264b 100644
1207 --- a/src/tokenpool-none.cc
1208 +++ b/src/tokenpool-none.cc
1210 -// Copyright 2016 Google Inc. All Rights Reserved.
1211 +// Copyright 2016-2017 Google Inc. All Rights Reserved.
1213 // Licensed under the Apache License, Version 2.0 (the "License");
1214 // you may not use this file except in compliance with the License.
1218 // No-op TokenPool implementation
1219 -struct TokenPool *TokenPool::Get(void) {
1220 +struct TokenPool *TokenPool::Get(bool ignore) {
1223 diff --git a/src/tokenpool.h b/src/tokenpool.h
1224 index 301e1998ee..878a0933c2 100644
1225 --- a/src/tokenpool.h
1226 +++ b/src/tokenpool.h
1228 -// Copyright 2016 Google Inc. All Rights Reserved.
1229 +// Copyright 2016-2017 Google Inc. All Rights Reserved.
1231 // Licensed under the Apache License, Version 2.0 (the "License");
1232 // you may not use this file except in compliance with the License.
1233 @@ -28,5 +28,5 @@ struct TokenPool {
1236 // returns NULL if token pool is not available
1237 - static struct TokenPool *Get(void);
1238 + static struct TokenPool *Get(bool ignore);
1241 From dfe4ca753caee65bf9041e2b4e883dfa172a5c6a Mon Sep 17 00:00:00 2001
1242 From: Stefan Becker <chemobejk@gmail.com>
1243 Date: Sun, 12 Nov 2017 18:04:12 +0200
1244 Subject: [PATCH 04/11] Honor -lN from MAKEFLAGS
1246 This emulates the behaviour of GNU make.
1248 - build: make a copy of max_load_average and pass it to TokenPool.
1249 - GNUmakeTokenPool: if we detect a jobserver and a valid -lN argument in
1250 MAKEFLAGS then set max_load_average to N.
1252 src/build.cc | 10 +++++++---
1253 src/tokenpool-gnu-make.cc | 19 +++++++++++++++----
1254 src/tokenpool-none.cc | 2 +-
1255 src/tokenpool.h | 2 +-
1256 4 files changed, 24 insertions(+), 9 deletions(-)
1258 diff --git a/src/build.cc b/src/build.cc
1259 index 406a84ec39..9e6272d035 100644
1262 @@ -464,13 +464,17 @@ struct RealCommandRunner : public CommandRunner {
1263 virtual void Abort();
1265 const BuildConfig& config_;
1266 + // copy of config_.max_load_average; can be modified by TokenPool setup
1267 + double max_load_average_;
1268 SubprocessSet subprocs_;
1270 map<const Subprocess*, Edge*> subproc_to_edge_;
1273 RealCommandRunner::RealCommandRunner(const BuildConfig& config) : config_(config) {
1274 - tokens_ = TokenPool::Get(config_.parallelism_from_cmdline);
1275 + max_load_average_ = config.max_load_average;
1276 + tokens_ = TokenPool::Get(config_.parallelism_from_cmdline,
1277 + max_load_average_);
1280 RealCommandRunner::~RealCommandRunner() {
1281 @@ -498,8 +502,8 @@ bool RealCommandRunner::CanRunMore() const {
1282 subprocs_.finished_.size()) < config_.parallelism);
1283 return parallelism_limit_not_reached
1284 && (subprocs_.running_.empty() ||
1285 - (config_.max_load_average <= 0.0f ||
1286 - GetLoadAverage() < config_.max_load_average));
1287 + (max_load_average_ <= 0.0f ||
1288 + GetLoadAverage() < max_load_average_));
1291 bool RealCommandRunner::AcquireToken() {
1292 diff --git a/src/tokenpool-gnu-make.cc b/src/tokenpool-gnu-make.cc
1293 index af4be05a31..fb654c4d88 100644
1294 --- a/src/tokenpool-gnu-make.cc
1295 +++ b/src/tokenpool-gnu-make.cc
1296 @@ -35,7 +35,7 @@ struct GNUmakeTokenPool : public TokenPool {
1297 virtual void Clear();
1298 virtual int GetMonitorFd();
1300 - bool Setup(bool ignore);
1301 + bool Setup(bool ignore, double& max_load_average);
1305 @@ -100,7 +100,7 @@ bool GNUmakeTokenPool::SetAlarmHandler() {
1309 -bool GNUmakeTokenPool::Setup(bool ignore) {
1310 +bool GNUmakeTokenPool::Setup(bool ignore, double& max_load_average) {
1311 const char *value = getenv("MAKEFLAGS");
1314 @@ -118,9 +118,20 @@ bool GNUmakeTokenPool::Setup(bool ignore) {
1317 SetAlarmHandler()) {
1318 + const char *l_arg = strstr(value, " -l");
1319 + int load_limit = -1;
1321 printf("ninja: using GNU make jobserver.\n");
1325 + // translate GNU make -lN to ninja -lN
1327 + (sscanf(l_arg + 3, "%d ", &load_limit) == 1) &&
1328 + (load_limit > 0)) {
1329 + max_load_average = load_limit;
1335 @@ -210,9 +221,9 @@ int GNUmakeTokenPool::GetMonitorFd() {
1339 -struct TokenPool *TokenPool::Get(bool ignore) {
1340 +struct TokenPool *TokenPool::Get(bool ignore, double& max_load_average) {
1341 GNUmakeTokenPool *tokenpool = new GNUmakeTokenPool;
1342 - if (tokenpool->Setup(ignore))
1343 + if (tokenpool->Setup(ignore, max_load_average))
1347 diff --git a/src/tokenpool-none.cc b/src/tokenpool-none.cc
1348 index 199b22264b..e8e25426c3 100644
1349 --- a/src/tokenpool-none.cc
1350 +++ b/src/tokenpool-none.cc
1354 // No-op TokenPool implementation
1355 -struct TokenPool *TokenPool::Get(bool ignore) {
1356 +struct TokenPool *TokenPool::Get(bool ignore, double& max_load_average) {
1359 diff --git a/src/tokenpool.h b/src/tokenpool.h
1360 index 878a0933c2..f9e8cc2ee0 100644
1361 --- a/src/tokenpool.h
1362 +++ b/src/tokenpool.h
1363 @@ -28,5 +28,5 @@ struct TokenPool {
1366 // returns NULL if token pool is not available
1367 - static struct TokenPool *Get(bool ignore);
1368 + static struct TokenPool *Get(bool ignore, double& max_load_average);
1371 From 1c10047fc6a3269ba42839da19361e09cbc06ff0 Mon Sep 17 00:00:00 2001
1372 From: Stefan Becker <chemobejk@gmail.com>
1373 Date: Wed, 6 Dec 2017 22:14:21 +0200
1374 Subject: [PATCH 05/11] Use LinePrinter for TokenPool messages
1376 - replace printf() with calls to LinePrinter
1377 - print GNU make jobserver message only when verbose build is requested
1380 src/tokenpool-gnu-make.cc | 22 ++++++++++++++++------
1381 src/tokenpool-none.cc | 4 +++-
1382 src/tokenpool.h | 4 +++-
1383 4 files changed, 23 insertions(+), 8 deletions(-)
1385 diff --git a/src/build.cc b/src/build.cc
1386 index 9e6272d035..662e4bd7be 100644
1389 @@ -474,6 +474,7 @@ struct RealCommandRunner : public CommandRunner {
1390 RealCommandRunner::RealCommandRunner(const BuildConfig& config) : config_(config) {
1391 max_load_average_ = config.max_load_average;
1392 tokens_ = TokenPool::Get(config_.parallelism_from_cmdline,
1393 + config_.verbosity == BuildConfig::VERBOSE,
1397 diff --git a/src/tokenpool-gnu-make.cc b/src/tokenpool-gnu-make.cc
1398 index fb654c4d88..b0d3e6ebc4 100644
1399 --- a/src/tokenpool-gnu-make.cc
1400 +++ b/src/tokenpool-gnu-make.cc
1405 +#include "line_printer.h"
1407 // TokenPool implementation for GNU make jobserver
1408 // (http://make.mad-scientist.net/papers/jobserver-implementation/)
1409 struct GNUmakeTokenPool : public TokenPool {
1410 @@ -35,7 +37,7 @@ struct GNUmakeTokenPool : public TokenPool {
1411 virtual void Clear();
1412 virtual int GetMonitorFd();
1414 - bool Setup(bool ignore, double& max_load_average);
1415 + bool Setup(bool ignore, bool verbose, double& max_load_average);
1419 @@ -100,7 +102,9 @@ bool GNUmakeTokenPool::SetAlarmHandler() {
1423 -bool GNUmakeTokenPool::Setup(bool ignore, double& max_load_average) {
1424 +bool GNUmakeTokenPool::Setup(bool ignore,
1426 + double& max_load_average) {
1427 const char *value = getenv("MAKEFLAGS");
1430 @@ -109,8 +113,10 @@ bool GNUmakeTokenPool::Setup(bool ignore, double& max_load_average) {
1432 jobserver = strstr(value, "--jobserver-auth=");
1434 + LinePrinter printer;
1437 - printf("ninja: warning: -jN forced on command line; ignoring GNU make jobserver.\n");
1438 + printer.PrintOnNewLine("ninja: warning: -jN forced on command line; ignoring GNU make jobserver.\n");
1442 @@ -121,7 +127,9 @@ bool GNUmakeTokenPool::Setup(bool ignore, double& max_load_average) {
1443 const char *l_arg = strstr(value, " -l");
1444 int load_limit = -1;
1446 - printf("ninja: using GNU make jobserver.\n");
1448 + printer.PrintOnNewLine("ninja: using GNU make jobserver.\n");
1453 @@ -221,9 +229,11 @@ int GNUmakeTokenPool::GetMonitorFd() {
1457 -struct TokenPool *TokenPool::Get(bool ignore, double& max_load_average) {
1458 +struct TokenPool *TokenPool::Get(bool ignore,
1460 + double& max_load_average) {
1461 GNUmakeTokenPool *tokenpool = new GNUmakeTokenPool;
1462 - if (tokenpool->Setup(ignore, max_load_average))
1463 + if (tokenpool->Setup(ignore, verbose, max_load_average))
1467 diff --git a/src/tokenpool-none.cc b/src/tokenpool-none.cc
1468 index e8e25426c3..1c1c499c8d 100644
1469 --- a/src/tokenpool-none.cc
1470 +++ b/src/tokenpool-none.cc
1474 // No-op TokenPool implementation
1475 -struct TokenPool *TokenPool::Get(bool ignore, double& max_load_average) {
1476 +struct TokenPool *TokenPool::Get(bool ignore,
1478 + double& max_load_average) {
1481 diff --git a/src/tokenpool.h b/src/tokenpool.h
1482 index f9e8cc2ee0..4bf477f20c 100644
1483 --- a/src/tokenpool.h
1484 +++ b/src/tokenpool.h
1485 @@ -28,5 +28,7 @@ struct TokenPool {
1488 // returns NULL if token pool is not available
1489 - static struct TokenPool *Get(bool ignore, double& max_load_average);
1490 + static struct TokenPool *Get(bool ignore,
1492 + double& max_load_average);
1495 From fdbf68416e3574add3bffd0b637d0694fbaba320 Mon Sep 17 00:00:00 2001
1496 From: Stefan Becker <chemobejk@gmail.com>
1497 Date: Sat, 7 Apr 2018 17:11:21 +0300
1498 Subject: [PATCH 06/11] Prepare PR for merging
1500 - fix Windows build error in no-op TokenPool implementation
1501 - improve Acquire() to block for a maximum of 100ms
1502 - address review comments
1505 src/tokenpool-gnu-make.cc | 53 +++++++++++++++++++++++++++++++++------
1506 src/tokenpool-none.cc | 7 +-----
1507 3 files changed, 49 insertions(+), 13 deletions(-)
1509 diff --git a/src/build.h b/src/build.h
1510 index dfde576573..66ddefb888 100644
1513 @@ -151,6 +151,8 @@ struct CommandRunner {
1514 bool success() const { return status == ExitSuccess; }
1516 /// Wait for a command to complete, or return false if interrupted.
1517 + /// If more_ready is true then the optional TokenPool is monitored too
1518 + /// and we return when a token becomes available.
1519 virtual bool WaitForCommand(Result* result, bool more_ready) = 0;
1521 virtual std::vector<Edge*> GetActiveEdges() { return std::vector<Edge*>(); }
1522 diff --git a/src/tokenpool-gnu-make.cc b/src/tokenpool-gnu-make.cc
1523 index b0d3e6ebc4..4132bb06d9 100644
1524 --- a/src/tokenpool-gnu-make.cc
1525 +++ b/src/tokenpool-gnu-make.cc
1527 -// Copyright 2016-2017 Google Inc. All Rights Reserved.
1528 +// Copyright 2016-2018 Google Inc. All Rights Reserved.
1530 // Licensed under the Apache License, Version 2.0 (the "License");
1531 // you may not use this file except in compliance with the License.
1536 +#include <sys/time.h>
1540 @@ -153,6 +154,15 @@ bool GNUmakeTokenPool::Acquire() {
1546 + // http://make.mad-scientist.net/papers/jobserver-implementation/
1548 + // for the reasoning behind the following code.
1550 + // Try to read one character from the pipe. Returns true on success.
1552 + // First check if read() would succeed without blocking.
1554 pollfd pollfds[] = {{rfd_, POLLIN, 0}};
1555 int ret = poll(pollfds, 1, 0);
1556 @@ -164,33 +174,62 @@ bool GNUmakeTokenPool::Acquire() {
1557 int ret = select(rfd_ + 1, &set, NULL, NULL, &timeout);
1560 + // Handle potential race condition:
1561 + // - the above check succeeded, i.e. read() should not block
1562 + // - the character disappears before we call read()
1564 + // Create a duplicate of rfd_. The duplicate file descriptor dup_rfd_
1565 + // can safely be closed by signal handlers without affecting rfd_.
1566 dup_rfd_ = dup(rfd_);
1568 if (dup_rfd_ != -1) {
1569 struct sigaction act, old_act;
1572 + // Temporarily replace SIGCHLD handler with our own
1573 memset(&act, 0, sizeof(act));
1574 act.sa_handler = CloseDupRfd;
1575 if (sigaction(SIGCHLD, &act, &old_act) == 0) {
1578 - // block until token read, child exits or timeout
1580 - ret = read(dup_rfd_, &buf, 1);
1582 + struct itimerval timeout;
1584 + // install a 100ms timeout that generates SIGALARM on expiration
1585 + memset(&timeout, 0, sizeof(timeout));
1586 + timeout.it_value.tv_usec = 100 * 1000; // [ms] -> [usec]
1587 + if (setitimer(ITIMER_REAL, &timeout, NULL) == 0) {
1590 + // Now try to read() from dup_rfd_. Return values from read():
1592 + // 1. token read -> 1
1593 + // 2. pipe closed -> 0
1594 + // 3. alarm expires -> -1 (EINTR)
1595 + // 4. child exits -> -1 (EINTR)
1596 + // 5. alarm expired before entering read() -> -1 (EBADF)
1597 + // 6. child exited before entering read() -> -1 (EBADF)
1598 + // 7. child exited before handler is installed -> go to 1 - 3
1599 + ret = read(dup_rfd_, &buf, 1);
1602 + memset(&timeout, 0, sizeof(timeout));
1603 + setitimer(ITIMER_REAL, &timeout, NULL);
1606 sigaction(SIGCHLD, &old_act, NULL);
1611 + // Case 1 from above list
1619 + // read() would block, i.e. no token available,
1620 + // cases 2-6 from above list or
1621 + // select() / poll() / dup() / sigaction() / setitimer() failed
1625 diff --git a/src/tokenpool-none.cc b/src/tokenpool-none.cc
1626 index 1c1c499c8d..4c592875b4 100644
1627 --- a/src/tokenpool-none.cc
1628 +++ b/src/tokenpool-none.cc
1630 -// Copyright 2016-2017 Google Inc. All Rights Reserved.
1631 +// Copyright 2016-2018 Google Inc. All Rights Reserved.
1633 // Licensed under the Apache License, Version 2.0 (the "License");
1634 // you may not use this file except in compliance with the License.
1637 #include "tokenpool.h"
1641 -#include <unistd.h>
1643 -#include <string.h>
1646 // No-op TokenPool implementation
1648 From ec6220a0baf7d3a6eaf1a2b75bf8960ddfe24c2f Mon Sep 17 00:00:00 2001
1649 From: Stefan Becker <chemobejk@gmail.com>
1650 Date: Fri, 25 May 2018 00:17:07 +0300
1651 Subject: [PATCH 07/11] Add tests for TokenPool
1654 - GetMonitorFd() API
1655 - implicit token and tokens in jobserver pipe
1656 - Acquire() / Reserve() / Release() protocol
1660 src/tokenpool_test.cc | 198 ++++++++++++++++++++++++++++++++++++++++++
1661 2 files changed, 199 insertions(+)
1662 create mode 100644 src/tokenpool_test.cc
1664 diff --git a/configure.py b/configure.py
1665 index db3492c93c..dc8a0066b7 100755
1668 @@ -590,6 +590,7 @@ def has_re2c():
1669 'string_piece_util_test',
1674 objs += cxx(name, variables=cxxvariables)
1675 if platform.is_windows():
1676 diff --git a/src/tokenpool_test.cc b/src/tokenpool_test.cc
1677 new file mode 100644
1678 index 0000000000..6c89064ca4
1680 +++ b/src/tokenpool_test.cc
1682 +// Copyright 2018 Google Inc. All Rights Reserved.
1684 +// Licensed under the Apache License, Version 2.0 (the "License");
1685 +// you may not use this file except in compliance with the License.
1686 +// You may obtain a copy of the License at
1688 +// http://www.apache.org/licenses/LICENSE-2.0
1690 +// Unless required by applicable law or agreed to in writing, software
1691 +// distributed under the License is distributed on an "AS IS" BASIS,
1692 +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1693 +// See the License for the specific language governing permissions and
1694 +// limitations under the License.
1696 +#include "tokenpool.h"
1702 +#include <stdlib.h>
1703 +#include <unistd.h>
1705 +#define ENVIRONMENT_CLEAR() unsetenv("MAKEFLAGS")
1706 +#define ENVIRONMENT_INIT(v) setenv("MAKEFLAGS", v, true);
1711 +const double kLoadAverageDefault = -1.23456789;
1713 +struct TokenPoolTest : public testing::Test {
1715 + TokenPool *tokens_;
1721 + virtual void SetUp() {
1722 + load_avg_ = kLoadAverageDefault;
1725 + ENVIRONMENT_CLEAR();
1726 + if (pipe(fds_) < 0)
1727 + ASSERT_TRUE(false);
1731 + void CreatePool(const char *format, bool ignore_jobserver) {
1734 + sprintf(buf_, format, fds_[0], fds_[1]);
1735 + ENVIRONMENT_INIT(buf_);
1738 + tokens_ = TokenPool::Get(ignore_jobserver, false, load_avg_);
1741 + void CreateDefaultPool() {
1742 + CreatePool("foo --jobserver-auth=%d,%d bar", false);
1745 + virtual void TearDown() {
1751 + ENVIRONMENT_CLEAR();
1756 +} // anonymous namespace
1758 +// verifies none implementation
1759 +TEST_F(TokenPoolTest, NoTokenPool) {
1760 + CreatePool(NULL, false);
1762 + EXPECT_EQ(NULL, tokens_);
1763 + EXPECT_EQ(kLoadAverageDefault, load_avg_);
1767 +TEST_F(TokenPoolTest, SuccessfulOldSetup) {
1769 + CreatePool("foo --jobserver-fds=%d,%d bar", false);
1771 + EXPECT_NE(NULL, tokens_);
1772 + EXPECT_EQ(kLoadAverageDefault, load_avg_);
1775 +TEST_F(TokenPoolTest, SuccessfulNewSetup) {
1777 + CreateDefaultPool();
1779 + EXPECT_NE(NULL, tokens_);
1780 + EXPECT_EQ(kLoadAverageDefault, load_avg_);
1783 +TEST_F(TokenPoolTest, IgnoreWithJN) {
1784 + CreatePool("foo --jobserver-auth=%d,%d bar", true);
1786 + EXPECT_EQ(NULL, tokens_);
1787 + EXPECT_EQ(kLoadAverageDefault, load_avg_);
1790 +TEST_F(TokenPoolTest, HonorLN) {
1791 + CreatePool("foo -l9 --jobserver-auth=%d,%d bar", false);
1793 + EXPECT_NE(NULL, tokens_);
1794 + EXPECT_EQ(9.0, load_avg_);
1797 +TEST_F(TokenPoolTest, MonitorFD) {
1798 + CreateDefaultPool();
1800 + ASSERT_NE(NULL, tokens_);
1801 + EXPECT_EQ(kLoadAverageDefault, load_avg_);
1803 + EXPECT_EQ(fds_[0], tokens_->GetMonitorFd());
1806 +TEST_F(TokenPoolTest, ImplicitToken) {
1807 + CreateDefaultPool();
1809 + ASSERT_NE(NULL, tokens_);
1810 + EXPECT_EQ(kLoadAverageDefault, load_avg_);
1812 + EXPECT_TRUE(tokens_->Acquire());
1813 + tokens_->Reserve();
1814 + EXPECT_FALSE(tokens_->Acquire());
1815 + tokens_->Release();
1816 + EXPECT_TRUE(tokens_->Acquire());
1819 +TEST_F(TokenPoolTest, TwoTokens) {
1820 + CreateDefaultPool();
1822 + ASSERT_NE(NULL, tokens_);
1823 + EXPECT_EQ(kLoadAverageDefault, load_avg_);
1826 + EXPECT_TRUE(tokens_->Acquire());
1827 + tokens_->Reserve();
1828 + EXPECT_FALSE(tokens_->Acquire());
1830 + // jobserver offers 2nd token
1831 + ASSERT_EQ(1u, write(fds_[1], "T", 1));
1832 + EXPECT_TRUE(tokens_->Acquire());
1833 + tokens_->Reserve();
1834 + EXPECT_FALSE(tokens_->Acquire());
1836 + // release 2nd token
1837 + tokens_->Release();
1838 + EXPECT_TRUE(tokens_->Acquire());
1840 + // release implict token - must return 2nd token back to jobserver
1841 + tokens_->Release();
1842 + EXPECT_TRUE(tokens_->Acquire());
1844 + // there must be one token in the pipe
1845 + EXPECT_EQ(1u, read(fds_[0], buf_, sizeof(buf_)));
1848 + EXPECT_TRUE(tokens_->Acquire());
1851 +TEST_F(TokenPoolTest, Clear) {
1852 + CreateDefaultPool();
1854 + ASSERT_NE(NULL, tokens_);
1855 + EXPECT_EQ(kLoadAverageDefault, load_avg_);
1858 + EXPECT_TRUE(tokens_->Acquire());
1859 + tokens_->Reserve();
1860 + EXPECT_FALSE(tokens_->Acquire());
1862 + // jobserver offers 2nd & 3rd token
1863 + ASSERT_EQ(2u, write(fds_[1], "TT", 2));
1864 + EXPECT_TRUE(tokens_->Acquire());
1865 + tokens_->Reserve();
1866 + EXPECT_TRUE(tokens_->Acquire());
1867 + tokens_->Reserve();
1868 + EXPECT_FALSE(tokens_->Acquire());
1871 + EXPECT_TRUE(tokens_->Acquire());
1873 + // there must be two tokens in the pipe
1874 + EXPECT_EQ(2u, read(fds_[0], buf_, sizeof(buf_)));
1877 + EXPECT_TRUE(tokens_->Acquire());
1881 From e59d8858327126d1593fd0b8e607975a79072e92 Mon Sep 17 00:00:00 2001
1882 From: Stefan Becker <chemobejk@gmail.com>
1883 Date: Thu, 24 May 2018 18:52:45 +0300
1884 Subject: [PATCH 08/11] Add tests for subprocess module
1886 - add TokenPoolTest stub to provide TokenPool::GetMonitorFd()
1888 * both tests set up a dummy GNUmake jobserver pipe
1889 * both tests call DoWork() with TokenPoolTest
1890 * test 1: verify that DoWork() detects when a token is available
1891 * test 2: verify that DoWork() works as before without a token
1892 - the tests are not compiled in under Windows
1894 src/subprocess_test.cc | 76 ++++++++++++++++++++++++++++++++++++++++++
1895 1 file changed, 76 insertions(+)
1897 diff --git a/src/subprocess_test.cc b/src/subprocess_test.cc
1898 index 4bc8083e26..6264c8bf11 100644
1899 --- a/src/subprocess_test.cc
1900 +++ b/src/subprocess_test.cc
1902 // limitations under the License.
1904 #include "subprocess.h"
1905 +#include "tokenpool.h"
1909 @@ -34,8 +35,23 @@ const char* kSimpleCommand = "cmd /c dir \\";
1910 const char* kSimpleCommand = "ls /";
1913 +struct TokenPoolTest : public TokenPool {
1914 + bool Acquire() { return false; }
1923 + int GetMonitorFd() { return _fd; }
1927 struct SubprocessTest : public testing::Test {
1928 SubprocessSet subprocs_;
1929 + TokenPoolTest tokens_;
1932 } // anonymous namespace
1933 @@ -280,3 +296,63 @@ TEST_F(SubprocessTest, ReadStdin) {
1934 ASSERT_EQ(1u, subprocs_.finished_.size());
1938 +// @TODO: remove once TokenPool implementation for Windows is available
1940 +TEST_F(SubprocessTest, TokenAvailable) {
1941 + Subprocess* subproc = subprocs_.Add(kSimpleCommand);
1942 + ASSERT_NE((Subprocess *) 0, subproc);
1944 + // simulate GNUmake jobserver pipe with 1 token
1946 + ASSERT_EQ(0u, pipe(fds));
1947 + tokens_._fd = fds[0];
1948 + ASSERT_EQ(1u, write(fds[1], "T", 1));
1950 + subprocs_.ResetTokenAvailable();
1951 + subprocs_.DoWork(&tokens_);
1953 + EXPECT_TRUE(subprocs_.IsTokenAvailable());
1954 + EXPECT_EQ(0u, subprocs_.finished_.size());
1956 + // remove token to let DoWork() wait for command again
1958 + ASSERT_EQ(1u, read(fds[0], &token, 1));
1960 + while (!subproc->Done()) {
1961 + subprocs_.DoWork(&tokens_);
1967 + EXPECT_EQ(ExitSuccess, subproc->Finish());
1968 + EXPECT_NE("", subproc->GetOutput());
1970 + EXPECT_EQ(1u, subprocs_.finished_.size());
1973 +TEST_F(SubprocessTest, TokenNotAvailable) {
1974 + Subprocess* subproc = subprocs_.Add(kSimpleCommand);
1975 + ASSERT_NE((Subprocess *) 0, subproc);
1977 + // simulate GNUmake jobserver pipe with 0 tokens
1979 + ASSERT_EQ(0u, pipe(fds));
1980 + tokens_._fd = fds[0];
1982 + subprocs_.ResetTokenAvailable();
1983 + while (!subproc->Done()) {
1984 + subprocs_.DoWork(&tokens_);
1990 + EXPECT_FALSE(subprocs_.IsTokenAvailable());
1991 + EXPECT_EQ(ExitSuccess, subproc->Finish());
1992 + EXPECT_NE("", subproc->GetOutput());
1994 + EXPECT_EQ(1u, subprocs_.finished_.size());
1998 From 0145e2d4db64ea6c21aeb371928e4071f65164eb Mon Sep 17 00:00:00 2001
1999 From: Stefan Becker <chemobejk@gmail.com>
2000 Date: Sat, 26 May 2018 23:17:51 +0300
2001 Subject: [PATCH 09/11] Add tests for build module
2003 Add tests that verify the token functionality of the builder main loop.
2004 We replace the default fake command runner with a special version where
2005 the tests can control each call to AcquireToken(), CanRunMore() and
2008 src/build_test.cc | 364 ++++++++++++++++++++++++++++++++++++++++++++++
2009 1 file changed, 364 insertions(+)
2011 diff --git a/src/build_test.cc b/src/build_test.cc
2012 index 7a5ff4015a..dd41dfbe1d 100644
2013 --- a/src/build_test.cc
2014 +++ b/src/build_test.cc
2019 +#include <stdarg.h>
2021 #include "build_log.h"
2022 #include "deps_log.h"
2023 @@ -3990,3 +3991,366 @@ TEST_F(BuildTest, ValidationWithCircularDependency) {
2024 EXPECT_FALSE(builder_.AddTarget("out", &err));
2025 EXPECT_EQ("dependency cycle: validate -> validate_in -> validate", err);
2028 +/// The token tests are concerned with the main loop functionality when
2029 +// the CommandRunner has an active TokenPool. It is therefore intentional
2030 +// that the plan doesn't complete and that builder_.Build() returns false!
2032 +/// Fake implementation of CommandRunner that simulates a TokenPool
2033 +struct FakeTokenCommandRunner : public CommandRunner {
2034 + explicit FakeTokenCommandRunner() {}
2036 + // CommandRunner impl
2037 + virtual bool CanRunMore() const;
2038 + virtual bool AcquireToken();
2039 + virtual bool StartCommand(Edge* edge);
2040 + virtual bool WaitForCommand(Result* result, bool more_ready);
2041 + virtual vector<Edge*> GetActiveEdges();
2042 + virtual void Abort();
2044 + vector<string> commands_ran_;
2045 + vector<Edge *> edges_;
2047 + vector<bool> acquire_token_;
2048 + vector<bool> can_run_more_;
2049 + vector<bool> wait_for_command_;
2052 +bool FakeTokenCommandRunner::CanRunMore() const {
2053 + if (can_run_more_.size() == 0) {
2054 + EXPECT_FALSE("unexpected call to CommandRunner::CanRunMore()");
2058 + bool result = can_run_more_[0];
2060 + // Unfortunately CanRunMore() isn't "const" for tests
2061 + const_cast<FakeTokenCommandRunner*>(this)->can_run_more_.erase(
2062 + const_cast<FakeTokenCommandRunner*>(this)->can_run_more_.begin()
2068 +bool FakeTokenCommandRunner::AcquireToken() {
2069 + if (acquire_token_.size() == 0) {
2070 + EXPECT_FALSE("unexpected call to CommandRunner::AcquireToken()");
2074 + bool result = acquire_token_[0];
2075 + acquire_token_.erase(acquire_token_.begin());
2079 +bool FakeTokenCommandRunner::StartCommand(Edge* edge) {
2080 + commands_ran_.push_back(edge->EvaluateCommand());
2081 + edges_.push_back(edge);
2085 +bool FakeTokenCommandRunner::WaitForCommand(Result* result, bool more_ready) {
2086 + if (wait_for_command_.size() == 0) {
2087 + EXPECT_FALSE("unexpected call to CommandRunner::WaitForCommand()");
2091 + bool expected = wait_for_command_[0];
2092 + if (expected != more_ready) {
2093 + EXPECT_EQ(expected, more_ready);
2096 + wait_for_command_.erase(wait_for_command_.begin());
2098 + if (edges_.size() == 0)
2101 + Edge* edge = edges_[0];
2102 + result->edge = edge;
2105 + (edge->rule().name() == "token-available")) {
2106 + result->status = ExitTokenAvailable;
2108 + edges_.erase(edges_.begin());
2109 + result->status = ExitSuccess;
2115 +vector<Edge*> FakeTokenCommandRunner::GetActiveEdges() {
2119 +void FakeTokenCommandRunner::Abort() {
2123 +struct BuildTokenTest : public BuildTest {
2124 + virtual void SetUp();
2125 + virtual void TearDown();
2127 + FakeTokenCommandRunner token_command_runner_;
2129 + void ExpectAcquireToken(int count, ...);
2130 + void ExpectCanRunMore(int count, ...);
2131 + void ExpectWaitForCommand(int count, ...);
2134 + void EnqueueBooleans(vector<bool>& booleans, int count, va_list ao);
2137 +void BuildTokenTest::SetUp() {
2138 + BuildTest::SetUp();
2140 + // replace FakeCommandRunner with FakeTokenCommandRunner
2141 + builder_.command_runner_.release();
2142 + builder_.command_runner_.reset(&token_command_runner_);
2144 +void BuildTokenTest::TearDown() {
2145 + EXPECT_EQ(0u, token_command_runner_.acquire_token_.size());
2146 + EXPECT_EQ(0u, token_command_runner_.can_run_more_.size());
2147 + EXPECT_EQ(0u, token_command_runner_.wait_for_command_.size());
2149 + BuildTest::TearDown();
2152 +void BuildTokenTest::ExpectAcquireToken(int count, ...) {
2154 + va_start(ap, count);
2155 + EnqueueBooleans(token_command_runner_.acquire_token_, count, ap);
2159 +void BuildTokenTest::ExpectCanRunMore(int count, ...) {
2161 + va_start(ap, count);
2162 + EnqueueBooleans(token_command_runner_.can_run_more_, count, ap);
2166 +void BuildTokenTest::ExpectWaitForCommand(int count, ...) {
2168 + va_start(ap, count);
2169 + EnqueueBooleans(token_command_runner_.wait_for_command_, count, ap);
2173 +void BuildTokenTest::EnqueueBooleans(vector<bool>& booleans, int count, va_list ap) {
2175 + int value = va_arg(ap, int);
2176 + booleans.push_back(!!value); // force bool
2180 +TEST_F(BuildTokenTest, CompleteNoWork) {
2181 + // plan should not execute anything
2184 + EXPECT_TRUE(builder_.Build(&err));
2185 + EXPECT_EQ("", err);
2187 + EXPECT_EQ(0u, token_command_runner_.commands_ran_.size());
2190 +TEST_F(BuildTokenTest, DoNotAquireToken) {
2191 + // plan should execute one command
2193 + EXPECT_TRUE(builder_.AddTarget("cat1", &err));
2194 + ASSERT_EQ("", err);
2196 + // pretend we can't run anything
2197 + ExpectCanRunMore(1, false);
2199 + EXPECT_FALSE(builder_.Build(&err));
2200 + EXPECT_EQ("stuck [this is a bug]", err);
2202 + EXPECT_EQ(0u, token_command_runner_.commands_ran_.size());
2205 +TEST_F(BuildTokenTest, DoNotStartWithoutToken) {
2206 + // plan should execute one command
2208 + EXPECT_TRUE(builder_.AddTarget("cat1", &err));
2209 + ASSERT_EQ("", err);
2211 + // we could run a command but do not have a token for it
2212 + ExpectCanRunMore(1, true);
2213 + ExpectAcquireToken(1, false);
2215 + EXPECT_FALSE(builder_.Build(&err));
2216 + EXPECT_EQ("stuck [this is a bug]", err);
2218 + EXPECT_EQ(0u, token_command_runner_.commands_ran_.size());
2221 +TEST_F(BuildTokenTest, CompleteOneStep) {
2222 + // plan should execute one command
2224 + EXPECT_TRUE(builder_.AddTarget("cat1", &err));
2225 + ASSERT_EQ("", err);
2227 + // allow running of one command
2228 + ExpectCanRunMore(1, true);
2229 + ExpectAcquireToken(1, true);
2230 + // block and wait for command to finalize
2231 + ExpectWaitForCommand(1, false);
2233 + EXPECT_TRUE(builder_.Build(&err));
2234 + EXPECT_EQ("", err);
2236 + EXPECT_EQ(1u, token_command_runner_.commands_ran_.size());
2237 + EXPECT_TRUE(token_command_runner_.commands_ran_[0] == "cat in1 > cat1");
2240 +TEST_F(BuildTokenTest, AcquireOneToken) {
2241 + // plan should execute more than one command
2243 + EXPECT_TRUE(builder_.AddTarget("cat12", &err));
2244 + ASSERT_EQ("", err);
2246 + // allow running of one command
2247 + ExpectCanRunMore(3, true, false, false);
2248 + ExpectAcquireToken(1, true);
2249 + // block and wait for command to finalize
2250 + ExpectWaitForCommand(1, false);
2252 + EXPECT_FALSE(builder_.Build(&err));
2253 + EXPECT_EQ("stuck [this is a bug]", err);
2255 + EXPECT_EQ(1u, token_command_runner_.commands_ran_.size());
2256 + // any of the two dependencies could have been executed
2257 + EXPECT_TRUE(token_command_runner_.commands_ran_[0] == "cat in1 > cat1" ||
2258 + token_command_runner_.commands_ran_[0] == "cat in1 in2 > cat2");
2261 +TEST_F(BuildTokenTest, WantTwoTokens) {
2262 + // plan should execute more than one command
2264 + EXPECT_TRUE(builder_.AddTarget("cat12", &err));
2265 + ASSERT_EQ("", err);
2267 + // allow running of one command
2268 + ExpectCanRunMore(3, true, true, false);
2269 + ExpectAcquireToken(2, true, false);
2270 + // wait for command to finalize or token to become available
2271 + ExpectWaitForCommand(1, true);
2273 + EXPECT_FALSE(builder_.Build(&err));
2274 + EXPECT_EQ("stuck [this is a bug]", err);
2276 + EXPECT_EQ(1u, token_command_runner_.commands_ran_.size());
2277 + // any of the two dependencies could have been executed
2278 + EXPECT_TRUE(token_command_runner_.commands_ran_[0] == "cat in1 > cat1" ||
2279 + token_command_runner_.commands_ran_[0] == "cat in1 in2 > cat2");
2282 +TEST_F(BuildTokenTest, CompleteTwoSteps) {
2283 + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
2284 +"build out1: cat in1\n"
2285 +"build out2: cat out1\n"));
2287 + // plan should execute more than one command
2289 + EXPECT_TRUE(builder_.AddTarget("out2", &err));
2290 + ASSERT_EQ("", err);
2292 + // allow running of two commands
2293 + ExpectCanRunMore(2, true, true);
2294 + ExpectAcquireToken(2, true, true);
2295 + // wait for commands to finalize
2296 + ExpectWaitForCommand(2, false, false);
2298 + EXPECT_TRUE(builder_.Build(&err));
2299 + EXPECT_EQ("", err);
2301 + EXPECT_EQ(2u, token_command_runner_.commands_ran_.size());
2302 + EXPECT_TRUE(token_command_runner_.commands_ran_[0] == "cat in1 > out1");
2303 + EXPECT_TRUE(token_command_runner_.commands_ran_[1] == "cat out1 > out2");
2306 +TEST_F(BuildTokenTest, TwoCommandsInParallel) {
2307 + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
2308 +"rule token-available\n"
2309 +" command = cat $in > $out\n"
2310 +"build out1: token-available in1\n"
2311 +"build out2: token-available in2\n"
2312 +"build out12: cat out1 out2\n"));
2314 + // plan should execute more than one command
2316 + EXPECT_TRUE(builder_.AddTarget("out12", &err));
2317 + ASSERT_EQ("", err);
2319 + // 1st command: token available -> allow running
2320 + // 2nd command: no token available but becomes available later
2321 + ExpectCanRunMore(4, true, true, true, false);
2322 + ExpectAcquireToken(3, true, false, true);
2323 + // 1st call waits for command to finalize or token to become available
2324 + // 2nd call waits for command to finalize
2325 + // 3rd call waits for command to finalize
2326 + ExpectWaitForCommand(3, true, false, false);
2328 + EXPECT_FALSE(builder_.Build(&err));
2329 + EXPECT_EQ("stuck [this is a bug]", err);
2331 + EXPECT_EQ(2u, token_command_runner_.commands_ran_.size());
2332 + EXPECT_TRUE((token_command_runner_.commands_ran_[0] == "cat in1 > out1" &&
2333 + token_command_runner_.commands_ran_[1] == "cat in2 > out2") ||
2334 + (token_command_runner_.commands_ran_[0] == "cat in2 > out2" &&
2335 + token_command_runner_.commands_ran_[1] == "cat in1 > out1"));
2338 +TEST_F(BuildTokenTest, CompleteThreeStepsSerial) {
2339 + // plan should execute more than one command
2341 + EXPECT_TRUE(builder_.AddTarget("cat12", &err));
2342 + ASSERT_EQ("", err);
2344 + // allow running of all commands
2345 + ExpectCanRunMore(4, true, true, true, true);
2346 + ExpectAcquireToken(4, true, false, true, true);
2347 + // wait for commands to finalize
2348 + ExpectWaitForCommand(3, true, false, false);
2350 + EXPECT_TRUE(builder_.Build(&err));
2351 + EXPECT_EQ("", err);
2353 + EXPECT_EQ(3u, token_command_runner_.commands_ran_.size());
2354 + EXPECT_TRUE((token_command_runner_.commands_ran_[0] == "cat in1 > cat1" &&
2355 + token_command_runner_.commands_ran_[1] == "cat in1 in2 > cat2") ||
2356 + (token_command_runner_.commands_ran_[0] == "cat in1 in2 > cat2" &&
2357 + token_command_runner_.commands_ran_[1] == "cat in1 > cat1" ));
2358 + EXPECT_TRUE(token_command_runner_.commands_ran_[2] == "cat cat1 cat2 > cat12");
2361 +TEST_F(BuildTokenTest, CompleteThreeStepsParallel) {
2362 + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
2363 +"rule token-available\n"
2364 +" command = cat $in > $out\n"
2365 +"build out1: token-available in1\n"
2366 +"build out2: token-available in2\n"
2367 +"build out12: cat out1 out2\n"));
2369 + // plan should execute more than one command
2371 + EXPECT_TRUE(builder_.AddTarget("out12", &err));
2372 + ASSERT_EQ("", err);
2374 + // allow running of all commands
2375 + ExpectCanRunMore(4, true, true, true, true);
2376 + ExpectAcquireToken(4, true, false, true, true);
2377 + // wait for commands to finalize
2378 + ExpectWaitForCommand(4, true, false, false, false);
2380 + EXPECT_TRUE(builder_.Build(&err));
2381 + EXPECT_EQ("", err);
2383 + EXPECT_EQ(3u, token_command_runner_.commands_ran_.size());
2384 + EXPECT_TRUE((token_command_runner_.commands_ran_[0] == "cat in1 > out1" &&
2385 + token_command_runner_.commands_ran_[1] == "cat in2 > out2") ||
2386 + (token_command_runner_.commands_ran_[0] == "cat in2 > out2" &&
2387 + token_command_runner_.commands_ran_[1] == "cat in1 > out1"));
2388 + EXPECT_TRUE(token_command_runner_.commands_ran_[2] == "cat out1 out2 > out12");
2391 From f016e5430c9123d34a73ea7ad28693b20ee59d6d Mon Sep 17 00:00:00 2001
2392 From: Stefan Becker <chemobejk@gmail.com>
2393 Date: Mon, 8 Oct 2018 17:47:50 +0300
2394 Subject: [PATCH 10/11] Add Win32 implementation for GNUmakeTokenPool
2396 GNU make uses a semaphore as jobserver protocol on Win32. See also
2398 https://www.gnu.org/software/make/manual/html_node/Windows-Jobserver.html
2400 Usage is pretty simple and straightforward, i.e. WaitForSingleObject()
2401 to obtain a token and ReleaseSemaphore() to return it.
2403 Unfortunately subprocess-win32.cc uses an I/O completion port (IOCP).
2404 IOCPs aren't waitable objects, i.e. we can't use WaitForMultipleObjects()
2405 to wait on the IOCP and the token semaphore at the same time.
2407 Therefore GNUmakeTokenPoolWin32 creates a child thread that waits on the
2408 token semaphore and posts a dummy I/O completion status on the IOCP when
2409 it was able to obtain a token. That unblocks SubprocessSet::DoWork() and
2410 it can then check if a token became available or not.
2412 - split existing GNUmakeTokenPool into common and platform bits
2413 - add GNUmakeTokenPool interface
2414 - move the Posix bits to GNUmakeTokenPoolPosix
2415 - add the Win32 bits as GNUmakeTokenPoolWin32
2416 - move Setup() method up to TokenPool interface
2417 - update Subprocess & TokenPool tests accordingly
2420 src/build.cc | 11 +-
2421 src/subprocess-win32.cc | 9 ++
2422 src/subprocess_test.cc | 34 ++++-
2423 src/tokenpool-gnu-make-posix.cc | 203 +++++++++++++++++++++++++++
2424 src/tokenpool-gnu-make-win32.cc | 237 ++++++++++++++++++++++++++++++++
2425 src/tokenpool-gnu-make.cc | 203 ++-------------------------
2426 src/tokenpool-gnu-make.h | 40 ++++++
2427 src/tokenpool-none.cc | 4 +-
2428 src/tokenpool.h | 18 ++-
2429 src/tokenpool_test.cc | 113 ++++++++++++---
2430 11 files changed, 653 insertions(+), 227 deletions(-)
2431 create mode 100644 src/tokenpool-gnu-make-posix.cc
2432 create mode 100644 src/tokenpool-gnu-make-win32.cc
2433 create mode 100644 src/tokenpool-gnu-make.h
2435 diff --git a/configure.py b/configure.py
2436 index dc8a0066b7..a239b90eef 100755
2439 @@ -517,12 +517,13 @@ def has_re2c():
2442 'string_piece_util',
2443 + 'tokenpool-gnu-make',
2446 objs += cxx(name, variables=cxxvariables)
2447 if platform.is_windows():
2448 for name in ['subprocess-win32',
2450 + 'tokenpool-gnu-make-win32',
2451 'includes_normalize-win32',
2452 'msvc_helper-win32',
2453 'msvc_helper_main-win32']:
2454 @@ -531,8 +532,9 @@ def has_re2c():
2455 objs += cxx('minidump-win32', variables=cxxvariables)
2456 objs += cc('getopt')
2458 - objs += cxx('subprocess-posix')
2459 - objs += cxx('tokenpool-gnu-make')
2460 + for name in ['subprocess-posix',
2461 + 'tokenpool-gnu-make-posix']:
2463 if platform.is_aix():
2464 objs += cc('getopt')
2465 if platform.is_msvc():
2466 diff --git a/src/build.cc b/src/build.cc
2467 index 662e4bd7be..20c3bdc2a0 100644
2470 @@ -473,9 +473,14 @@ struct RealCommandRunner : public CommandRunner {
2472 RealCommandRunner::RealCommandRunner(const BuildConfig& config) : config_(config) {
2473 max_load_average_ = config.max_load_average;
2474 - tokens_ = TokenPool::Get(config_.parallelism_from_cmdline,
2475 - config_.verbosity == BuildConfig::VERBOSE,
2476 - max_load_average_);
2477 + if ((tokens_ = TokenPool::Get()) != NULL) {
2478 + if (!tokens_->Setup(config_.parallelism_from_cmdline,
2479 + config_.verbosity == BuildConfig::VERBOSE,
2480 + max_load_average_)) {
2487 RealCommandRunner::~RealCommandRunner() {
2488 diff --git a/src/subprocess-win32.cc b/src/subprocess-win32.cc
2489 index 66d2c2c430..ce3e2c20a4 100644
2490 --- a/src/subprocess-win32.cc
2491 +++ b/src/subprocess-win32.cc
2493 // limitations under the License.
2495 #include "subprocess.h"
2496 +#include "tokenpool.h"
2500 @@ -256,6 +257,9 @@ bool SubprocessSet::DoWork(struct TokenPool* tokens) {
2501 Subprocess* subproc;
2502 OVERLAPPED* overlapped;
2505 + tokens->WaitForTokenAvailability(ioport_);
2507 if (!GetQueuedCompletionStatus(ioport_, &bytes_read, (PULONG_PTR)&subproc,
2508 &overlapped, INFINITE)) {
2509 if (GetLastError() != ERROR_BROKEN_PIPE)
2510 @@ -266,6 +270,11 @@ bool SubprocessSet::DoWork(struct TokenPool* tokens) {
2511 // delivered by NotifyInterrupted above.
2514 + if (tokens && tokens->TokenIsAvailable((ULONG_PTR)subproc)) {
2515 + token_available_ = true;
2519 subproc->OnPipeReady();
2521 if (subproc->Done()) {
2522 diff --git a/src/subprocess_test.cc b/src/subprocess_test.cc
2523 index 6264c8bf11..f625963462 100644
2524 --- a/src/subprocess_test.cc
2525 +++ b/src/subprocess_test.cc
2526 @@ -40,9 +40,16 @@ struct TokenPoolTest : public TokenPool {
2530 + bool Setup(bool ignore_unused, bool verbose, double& max_load_average) { return false; }
2534 + bool _token_available;
2535 + void WaitForTokenAvailability(HANDLE ioport) {
2536 + if (_token_available)
2537 + // unblock GetQueuedCompletionStatus()
2538 + PostQueuedCompletionStatus(ioport, 0, (ULONG_PTR) this, NULL);
2540 + bool TokenIsAvailable(ULONG_PTR key) { return key == (ULONG_PTR) this; }
2543 int GetMonitorFd() { return _fd; }
2544 @@ -297,34 +304,48 @@ TEST_F(SubprocessTest, ReadStdin) {
2548 -// @TODO: remove once TokenPool implementation for Windows is available
2550 TEST_F(SubprocessTest, TokenAvailable) {
2551 Subprocess* subproc = subprocs_.Add(kSimpleCommand);
2552 ASSERT_NE((Subprocess *) 0, subproc);
2554 // simulate GNUmake jobserver pipe with 1 token
2556 + tokens_._token_available = true;
2559 ASSERT_EQ(0u, pipe(fds));
2560 tokens_._fd = fds[0];
2561 ASSERT_EQ(1u, write(fds[1], "T", 1));
2564 subprocs_.ResetTokenAvailable();
2565 subprocs_.DoWork(&tokens_);
2567 + tokens_._token_available = false;
2568 + // we need to loop here as we have no conrol where the token
2569 + // I/O completion post ends up in the queue
2570 + while (!subproc->Done() && !subprocs_.IsTokenAvailable()) {
2571 + subprocs_.DoWork(&tokens_);
2575 EXPECT_TRUE(subprocs_.IsTokenAvailable());
2576 EXPECT_EQ(0u, subprocs_.finished_.size());
2578 // remove token to let DoWork() wait for command again
2581 ASSERT_EQ(1u, read(fds[0], &token, 1));
2584 while (!subproc->Done()) {
2585 subprocs_.DoWork(&tokens_);
2593 EXPECT_EQ(ExitSuccess, subproc->Finish());
2594 EXPECT_NE("", subproc->GetOutput());
2595 @@ -337,17 +358,23 @@ TEST_F(SubprocessTest, TokenNotAvailable) {
2596 ASSERT_NE((Subprocess *) 0, subproc);
2598 // simulate GNUmake jobserver pipe with 0 tokens
2600 + tokens_._token_available = false;
2603 ASSERT_EQ(0u, pipe(fds));
2604 tokens_._fd = fds[0];
2607 subprocs_.ResetTokenAvailable();
2608 while (!subproc->Done()) {
2609 subprocs_.DoWork(&tokens_);
2617 EXPECT_FALSE(subprocs_.IsTokenAvailable());
2618 EXPECT_EQ(ExitSuccess, subproc->Finish());
2619 @@ -355,4 +382,3 @@ TEST_F(SubprocessTest, TokenNotAvailable) {
2621 EXPECT_EQ(1u, subprocs_.finished_.size());
2624 diff --git a/src/tokenpool-gnu-make-posix.cc b/src/tokenpool-gnu-make-posix.cc
2625 new file mode 100644
2626 index 0000000000..70d84bfff7
2628 +++ b/src/tokenpool-gnu-make-posix.cc
2630 +// Copyright 2016-2018 Google Inc. All Rights Reserved.
2632 +// Licensed under the Apache License, Version 2.0 (the "License");
2633 +// you may not use this file except in compliance with the License.
2634 +// You may obtain a copy of the License at
2636 +// http://www.apache.org/licenses/LICENSE-2.0
2638 +// Unless required by applicable law or agreed to in writing, software
2639 +// distributed under the License is distributed on an "AS IS" BASIS,
2640 +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2641 +// See the License for the specific language governing permissions and
2642 +// limitations under the License.
2644 +#include "tokenpool-gnu-make.h"
2649 +#include <unistd.h>
2650 +#include <signal.h>
2651 +#include <sys/time.h>
2653 +#include <string.h>
2654 +#include <stdlib.h>
2656 +// TokenPool implementation for GNU make jobserver - POSIX implementation
2657 +// (http://make.mad-scientist.net/papers/jobserver-implementation/)
2658 +struct GNUmakeTokenPoolPosix : public GNUmakeTokenPool {
2659 + GNUmakeTokenPoolPosix();
2660 + virtual ~GNUmakeTokenPoolPosix();
2662 + virtual int GetMonitorFd();
2664 + virtual const char *GetEnv(const char *name) { return getenv(name); };
2665 + virtual bool ParseAuth(const char *jobserver);
2666 + virtual bool AcquireToken();
2667 + virtual bool ReturnToken();
2673 + struct sigaction old_act_;
2676 + static int dup_rfd_;
2677 + static void CloseDupRfd(int signum);
2679 + bool CheckFd(int fd);
2680 + bool SetAlarmHandler();
2683 +GNUmakeTokenPoolPosix::GNUmakeTokenPoolPosix() : rfd_(-1), wfd_(-1), restore_(false) {
2686 +GNUmakeTokenPoolPosix::~GNUmakeTokenPoolPosix() {
2689 + sigaction(SIGALRM, &old_act_, NULL);
2692 +bool GNUmakeTokenPoolPosix::CheckFd(int fd) {
2695 + int ret = fcntl(fd, F_GETFD);
2701 +int GNUmakeTokenPoolPosix::dup_rfd_ = -1;
2703 +void GNUmakeTokenPoolPosix::CloseDupRfd(int signum) {
2708 +bool GNUmakeTokenPoolPosix::SetAlarmHandler() {
2709 + struct sigaction act;
2710 + memset(&act, 0, sizeof(act));
2711 + act.sa_handler = CloseDupRfd;
2712 + if (sigaction(SIGALRM, &act, &old_act_) < 0) {
2713 + perror("sigaction:");
2721 +bool GNUmakeTokenPoolPosix::ParseAuth(const char *jobserver) {
2724 + if ((sscanf(jobserver, "%*[^=]=%d,%d", &rfd, &wfd) == 2) &&
2727 + SetAlarmHandler()) {
2736 +bool GNUmakeTokenPoolPosix::AcquireToken() {
2739 + // http://make.mad-scientist.net/papers/jobserver-implementation/
2741 + // for the reasoning behind the following code.
2743 + // Try to read one character from the pipe. Returns true on success.
2745 + // First check if read() would succeed without blocking.
2747 + pollfd pollfds[] = {{rfd_, POLLIN, 0}};
2748 + int ret = poll(pollfds, 1, 0);
2751 + struct timeval timeout = { 0, 0 };
2753 + FD_SET(rfd_, &set);
2754 + int ret = select(rfd_ + 1, &set, NULL, NULL, &timeout);
2757 + // Handle potential race condition:
2758 + // - the above check succeeded, i.e. read() should not block
2759 + // - the character disappears before we call read()
2761 + // Create a duplicate of rfd_. The duplicate file descriptor dup_rfd_
2762 + // can safely be closed by signal handlers without affecting rfd_.
2763 + dup_rfd_ = dup(rfd_);
2765 + if (dup_rfd_ != -1) {
2766 + struct sigaction act, old_act;
2769 + // Temporarily replace SIGCHLD handler with our own
2770 + memset(&act, 0, sizeof(act));
2771 + act.sa_handler = CloseDupRfd;
2772 + if (sigaction(SIGCHLD, &act, &old_act) == 0) {
2773 + struct itimerval timeout;
2775 + // install a 100ms timeout that generates SIGALARM on expiration
2776 + memset(&timeout, 0, sizeof(timeout));
2777 + timeout.it_value.tv_usec = 100 * 1000; // [ms] -> [usec]
2778 + if (setitimer(ITIMER_REAL, &timeout, NULL) == 0) {
2781 + // Now try to read() from dup_rfd_. Return values from read():
2783 + // 1. token read -> 1
2784 + // 2. pipe closed -> 0
2785 + // 3. alarm expires -> -1 (EINTR)
2786 + // 4. child exits -> -1 (EINTR)
2787 + // 5. alarm expired before entering read() -> -1 (EBADF)
2788 + // 6. child exited before entering read() -> -1 (EBADF)
2789 + // 7. child exited before handler is installed -> go to 1 - 3
2790 + ret = read(dup_rfd_, &buf, 1);
2793 + memset(&timeout, 0, sizeof(timeout));
2794 + setitimer(ITIMER_REAL, &timeout, NULL);
2797 + sigaction(SIGCHLD, &old_act, NULL);
2802 + // Case 1 from above list
2808 + // read() would block, i.e. no token available,
2809 + // cases 2-6 from above list or
2810 + // select() / poll() / dup() / sigaction() / setitimer() failed
2814 +bool GNUmakeTokenPoolPosix::ReturnToken() {
2815 + const char buf = '+';
2817 + int ret = write(wfd_, &buf, 1);
2820 + if ((ret != -1) || (errno != EINTR))
2822 + // write got interrupted - retry
2826 +int GNUmakeTokenPoolPosix::GetMonitorFd() {
2830 +struct TokenPool *TokenPool::Get() {
2831 + return new GNUmakeTokenPoolPosix;
2833 diff --git a/src/tokenpool-gnu-make-win32.cc b/src/tokenpool-gnu-make-win32.cc
2834 new file mode 100644
2835 index 0000000000..2719f2c1fc
2837 +++ b/src/tokenpool-gnu-make-win32.cc
2839 +// Copyright 2018 Google Inc. All Rights Reserved.
2841 +// Licensed under the Apache License, Version 2.0 (the "License");
2842 +// you may not use this file except in compliance with the License.
2843 +// You may obtain a copy of the License at
2845 +// http://www.apache.org/licenses/LICENSE-2.0
2847 +// Unless required by applicable law or agreed to in writing, software
2848 +// distributed under the License is distributed on an "AS IS" BASIS,
2849 +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2850 +// See the License for the specific language governing permissions and
2851 +// limitations under the License.
2853 +#include "tokenpool-gnu-make.h"
2855 +// always include first to make sure other headers do the correct thing...
2856 +#include <windows.h>
2859 +#include <stdlib.h>
2860 +#include <string.h>
2864 +// TokenPool implementation for GNU make jobserver - Win32 implementation
2865 +// (https://www.gnu.org/software/make/manual/html_node/Windows-Jobserver.html)
2866 +struct GNUmakeTokenPoolWin32 : public GNUmakeTokenPool {
2867 + GNUmakeTokenPoolWin32();
2868 + virtual ~GNUmakeTokenPoolWin32();
2870 + virtual void WaitForTokenAvailability(HANDLE ioport);
2871 + virtual bool TokenIsAvailable(ULONG_PTR key);
2873 + virtual const char *GetEnv(const char *name);
2874 + virtual bool ParseAuth(const char *jobserver);
2875 + virtual bool AcquireToken();
2876 + virtual bool ReturnToken();
2879 + // Semaphore for GNU make jobserver protocol
2880 + HANDLE semaphore_jobserver_;
2881 + // Semaphore Child -> Parent
2882 + // - child releases it before entering wait on jobserver semaphore
2883 + // - parent blocks on it to know when child enters wait
2884 + HANDLE semaphore_enter_wait_;
2885 + // Semaphore Parent -> Child
2886 + // - parent releases it to allow child to restart loop
2887 + // - child blocks on it to know when to restart loop
2888 + HANDLE semaphore_restart_;
2889 + // set to false if child should exit loop and terminate thread
2893 + // I/O completion port from SubprocessSet
2897 + DWORD SemaphoreThread();
2898 + void ReleaseSemaphore(HANDLE semaphore);
2899 + void WaitForObject(HANDLE object);
2900 + static DWORD WINAPI SemaphoreThreadWrapper(LPVOID param);
2901 + static void NoopAPCFunc(ULONG_PTR param);
2904 +GNUmakeTokenPoolWin32::GNUmakeTokenPoolWin32() : semaphore_jobserver_(NULL),
2905 + semaphore_enter_wait_(NULL),
2906 + semaphore_restart_(NULL),
2912 +GNUmakeTokenPoolWin32::~GNUmakeTokenPoolWin32() {
2914 + CloseHandle(semaphore_jobserver_);
2915 + semaphore_jobserver_ = NULL;
2918 + // tell child thread to exit
2920 + ReleaseSemaphore(semaphore_restart_);
2922 + // wait for child thread to exit
2923 + WaitForObject(child_);
2924 + CloseHandle(child_);
2928 + if (semaphore_restart_) {
2929 + CloseHandle(semaphore_restart_);
2930 + semaphore_restart_ = NULL;
2933 + if (semaphore_enter_wait_) {
2934 + CloseHandle(semaphore_enter_wait_);
2935 + semaphore_enter_wait_ = NULL;
2939 +const char *GNUmakeTokenPoolWin32::GetEnv(const char *name) {
2940 + // getenv() does not work correctly together with tokenpool_tests.cc
2941 + static char buffer[MAX_PATH + 1];
2942 + if (GetEnvironmentVariable("MAKEFLAGS", buffer, sizeof(buffer)) == 0)
2947 +bool GNUmakeTokenPoolWin32::ParseAuth(const char *jobserver) {
2948 + // match "--jobserver-auth=gmake_semaphore_<INTEGER>..."
2949 + const char *start = strchr(jobserver, '=');
2951 + const char *end = start;
2955 + while ((c = *++end) != '\0')
2956 + if (!(isalnum(c) || (c == '_')))
2958 + len = end - start; // includes string terminator in count
2960 + if ((len > 1) && ((auth = (char *)malloc(len)) != NULL)) {
2961 + strncpy(auth, start + 1, len - 1);
2962 + auth[len - 1] = '\0';
2964 + if ((semaphore_jobserver_ = OpenSemaphore(SEMAPHORE_ALL_ACCESS, /* Semaphore access setting */
2965 + FALSE, /* Child processes DON'T inherit */
2966 + auth /* Semaphore name */
2979 +bool GNUmakeTokenPoolWin32::AcquireToken() {
2980 + return WaitForSingleObject(semaphore_jobserver_, 0) == WAIT_OBJECT_0;
2983 +bool GNUmakeTokenPoolWin32::ReturnToken() {
2984 + ReleaseSemaphore(semaphore_jobserver_);
2988 +DWORD GNUmakeTokenPoolWin32::SemaphoreThread() {
2989 + while (running_) {
2990 + // indicate to parent that we are entering wait
2991 + ReleaseSemaphore(semaphore_enter_wait_);
2993 + // alertable wait forever on token semaphore
2994 + if (WaitForSingleObjectEx(semaphore_jobserver_, INFINITE, TRUE) == WAIT_OBJECT_0) {
2995 + // release token again for AcquireToken()
2996 + ReleaseSemaphore(semaphore_jobserver_);
2998 + // indicate to parent on ioport that a token might be available
2999 + if (!PostQueuedCompletionStatus(ioport_, 0, (ULONG_PTR) this, NULL))
3000 + Win32Fatal("PostQueuedCompletionStatus");
3003 + // wait for parent to allow loop restart
3004 + WaitForObject(semaphore_restart_);
3005 + // semaphore is now in nonsignaled state again for next run...
3011 +DWORD WINAPI GNUmakeTokenPoolWin32::SemaphoreThreadWrapper(LPVOID param) {
3012 + GNUmakeTokenPoolWin32 *This = (GNUmakeTokenPoolWin32 *) param;
3013 + return This->SemaphoreThread();
3016 +void GNUmakeTokenPoolWin32::NoopAPCFunc(ULONG_PTR param) {
3019 +void GNUmakeTokenPoolWin32::WaitForTokenAvailability(HANDLE ioport) {
3020 + if (child_ == NULL) {
3021 + // first invocation
3023 + // subprocess-win32.cc uses I/O completion port (IOCP) which can't be
3024 + // used as a waitable object. Therefore we can't use WaitMultipleObjects()
3025 + // to wait on the IOCP and the token semaphore at the same time. Create
3026 + // a child thread that waits on the semaphore and posts an I/O completion
3029 + // create both semaphores in nonsignaled state
3030 + if ((semaphore_enter_wait_ = CreateSemaphore(NULL, 0, 1, NULL))
3032 + Win32Fatal("CreateSemaphore/enter_wait");
3033 + if ((semaphore_restart_ = CreateSemaphore(NULL, 0, 1, NULL))
3035 + Win32Fatal("CreateSemaphore/restart");
3037 + // start child thread
3039 + if ((child_ = CreateThread(NULL, 0, &SemaphoreThreadWrapper, this, 0, NULL))
3041 + Win32Fatal("CreateThread");
3044 + // all further invocations - allow child thread to loop
3045 + ReleaseSemaphore(semaphore_restart_);
3048 + // wait for child thread to enter wait
3049 + WaitForObject(semaphore_enter_wait_);
3050 + // semaphore is now in nonsignaled state again for next run...
3052 + // now SubprocessSet::DoWork() can enter GetQueuedCompletionStatus()...
3055 +bool GNUmakeTokenPoolWin32::TokenIsAvailable(ULONG_PTR key) {
3056 + // alert child thread to break wait on token semaphore
3057 + QueueUserAPC(&NoopAPCFunc, child_, (ULONG_PTR)NULL);
3059 + // return true when GetQueuedCompletionStatus() returned our key
3060 + return key == (ULONG_PTR) this;
3063 +void GNUmakeTokenPoolWin32::ReleaseSemaphore(HANDLE semaphore) {
3064 + if (!::ReleaseSemaphore(semaphore, 1, NULL))
3065 + Win32Fatal("ReleaseSemaphore");
3068 +void GNUmakeTokenPoolWin32::WaitForObject(HANDLE object) {
3069 + if (WaitForSingleObject(object, INFINITE) != WAIT_OBJECT_0)
3070 + Win32Fatal("WaitForSingleObject");
3073 +struct TokenPool *TokenPool::Get() {
3074 + return new GNUmakeTokenPoolWin32;
3076 diff --git a/src/tokenpool-gnu-make.cc b/src/tokenpool-gnu-make.cc
3077 index 4132bb06d9..92ff611721 100644
3078 --- a/src/tokenpool-gnu-make.cc
3079 +++ b/src/tokenpool-gnu-make.cc
3080 @@ -12,101 +12,26 @@
3081 // See the License for the specific language governing permissions and
3082 // limitations under the License.
3084 -#include "tokenpool.h"
3085 +#include "tokenpool-gnu-make.h"
3090 -#include <unistd.h>
3091 -#include <signal.h>
3092 -#include <sys/time.h>
3093 +#include <stdlib.h>
3096 -#include <stdlib.h>
3098 #include "line_printer.h"
3100 -// TokenPool implementation for GNU make jobserver
3101 -// (http://make.mad-scientist.net/papers/jobserver-implementation/)
3102 -struct GNUmakeTokenPool : public TokenPool {
3103 - GNUmakeTokenPool();
3104 - virtual ~GNUmakeTokenPool();
3106 - virtual bool Acquire();
3107 - virtual void Reserve();
3108 - virtual void Release();
3109 - virtual void Clear();
3110 - virtual int GetMonitorFd();
3112 - bool Setup(bool ignore, bool verbose, double& max_load_average);
3124 - struct sigaction old_act_;
3127 - static int dup_rfd_;
3128 - static void CloseDupRfd(int signum);
3130 - bool CheckFd(int fd);
3131 - bool SetAlarmHandler();
3137 +// TokenPool implementation for GNU make jobserver - common bits
3138 // every instance owns an implicit token -> available_ == 1
3139 -GNUmakeTokenPool::GNUmakeTokenPool() : available_(1), used_(0),
3140 - rfd_(-1), wfd_(-1), restore_(false) {
3141 +GNUmakeTokenPool::GNUmakeTokenPool() : available_(1), used_(0) {
3144 GNUmakeTokenPool::~GNUmakeTokenPool() {
3147 - sigaction(SIGALRM, &old_act_, NULL);
3150 -bool GNUmakeTokenPool::CheckFd(int fd) {
3153 - int ret = fcntl(fd, F_GETFD);
3159 -int GNUmakeTokenPool::dup_rfd_ = -1;
3161 -void GNUmakeTokenPool::CloseDupRfd(int signum) {
3166 -bool GNUmakeTokenPool::SetAlarmHandler() {
3167 - struct sigaction act;
3168 - memset(&act, 0, sizeof(act));
3169 - act.sa_handler = CloseDupRfd;
3170 - if (sigaction(SIGALRM, &act, &old_act_) < 0) {
3171 - perror("sigaction:");
3179 bool GNUmakeTokenPool::Setup(bool ignore,
3181 double& max_load_average) {
3182 - const char *value = getenv("MAKEFLAGS");
3183 + const char *value = GetEnv("MAKEFLAGS");
3186 const char *jobserver = strstr(value, "--jobserver-fds=");
3187 @@ -119,20 +44,13 @@ bool GNUmakeTokenPool::Setup(bool ignore,
3189 printer.PrintOnNewLine("ninja: warning: -jN forced on command line; ignoring GNU make jobserver.\n");
3193 - if ((sscanf(jobserver, "%*[^=]=%d,%d", &rfd, &wfd) == 2) &&
3196 - SetAlarmHandler()) {
3197 + if (ParseAuth(jobserver)) {
3198 const char *l_arg = strstr(value, " -l");
3199 int load_limit = -1;
3202 printer.PrintOnNewLine("ninja: using GNU make jobserver.\n");
3207 // translate GNU make -lN to ninja -lN
3209 @@ -154,83 +72,14 @@ bool GNUmakeTokenPool::Acquire() {
3215 - // http://make.mad-scientist.net/papers/jobserver-implementation/
3217 - // for the reasoning behind the following code.
3219 - // Try to read one character from the pipe. Returns true on success.
3221 - // First check if read() would succeed without blocking.
3223 - pollfd pollfds[] = {{rfd_, POLLIN, 0}};
3224 - int ret = poll(pollfds, 1, 0);
3227 - struct timeval timeout = { 0, 0 };
3229 - FD_SET(rfd_, &set);
3230 - int ret = select(rfd_ + 1, &set, NULL, NULL, &timeout);
3233 - // Handle potential race condition:
3234 - // - the above check succeeded, i.e. read() should not block
3235 - // - the character disappears before we call read()
3237 - // Create a duplicate of rfd_. The duplicate file descriptor dup_rfd_
3238 - // can safely be closed by signal handlers without affecting rfd_.
3239 - dup_rfd_ = dup(rfd_);
3241 - if (dup_rfd_ != -1) {
3242 - struct sigaction act, old_act;
3245 - // Temporarily replace SIGCHLD handler with our own
3246 - memset(&act, 0, sizeof(act));
3247 - act.sa_handler = CloseDupRfd;
3248 - if (sigaction(SIGCHLD, &act, &old_act) == 0) {
3249 - struct itimerval timeout;
3251 - // install a 100ms timeout that generates SIGALARM on expiration
3252 - memset(&timeout, 0, sizeof(timeout));
3253 - timeout.it_value.tv_usec = 100 * 1000; // [ms] -> [usec]
3254 - if (setitimer(ITIMER_REAL, &timeout, NULL) == 0) {
3257 - // Now try to read() from dup_rfd_. Return values from read():
3259 - // 1. token read -> 1
3260 - // 2. pipe closed -> 0
3261 - // 3. alarm expires -> -1 (EINTR)
3262 - // 4. child exits -> -1 (EINTR)
3263 - // 5. alarm expired before entering read() -> -1 (EBADF)
3264 - // 6. child exited before entering read() -> -1 (EBADF)
3265 - // 7. child exited before handler is installed -> go to 1 - 3
3266 - ret = read(dup_rfd_, &buf, 1);
3269 - memset(&timeout, 0, sizeof(timeout));
3270 - setitimer(ITIMER_REAL, &timeout, NULL);
3273 - sigaction(SIGCHLD, &old_act, NULL);
3278 - // Case 1 from above list
3284 + if (AcquireToken()) {
3289 + // no token available
3293 - // read() would block, i.e. no token available,
3294 - // cases 2-6 from above list or
3295 - // select() / poll() / dup() / sigaction() / setitimer() failed
3299 void GNUmakeTokenPool::Reserve() {
3300 @@ -239,15 +88,8 @@ void GNUmakeTokenPool::Reserve() {
3303 void GNUmakeTokenPool::Return() {
3304 - const char buf = '+';
3306 - int ret = write(wfd_, &buf, 1);
3309 - if ((ret != -1) || (errno != EINTR))
3311 - // write got interrupted - retry
3313 + if (ReturnToken())
3317 void GNUmakeTokenPool::Release() {
3318 @@ -263,18 +105,3 @@ void GNUmakeTokenPool::Clear() {
3319 while (available_ > 1)
3323 -int GNUmakeTokenPool::GetMonitorFd() {
3327 -struct TokenPool *TokenPool::Get(bool ignore,
3329 - double& max_load_average) {
3330 - GNUmakeTokenPool *tokenpool = new GNUmakeTokenPool;
3331 - if (tokenpool->Setup(ignore, verbose, max_load_average))
3337 diff --git a/src/tokenpool-gnu-make.h b/src/tokenpool-gnu-make.h
3338 new file mode 100644
3339 index 0000000000..d3852088e2
3341 +++ b/src/tokenpool-gnu-make.h
3343 +// Copyright 2016-2018 Google Inc. All Rights Reserved.
3345 +// Licensed under the Apache License, Version 2.0 (the "License");
3346 +// you may not use this file except in compliance with the License.
3347 +// You may obtain a copy of the License at
3349 +// http://www.apache.org/licenses/LICENSE-2.0
3351 +// Unless required by applicable law or agreed to in writing, software
3352 +// distributed under the License is distributed on an "AS IS" BASIS,
3353 +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3354 +// See the License for the specific language governing permissions and
3355 +// limitations under the License.
3357 +#include "tokenpool.h"
3359 +// interface to GNU make token pool
3360 +struct GNUmakeTokenPool : public TokenPool {
3361 + GNUmakeTokenPool();
3362 + virtual ~GNUmakeTokenPool();
3364 + // token pool implementation
3365 + virtual bool Acquire();
3366 + virtual void Reserve();
3367 + virtual void Release();
3368 + virtual void Clear();
3369 + virtual bool Setup(bool ignore, bool verbose, double& max_load_average);
3371 + // platform specific implementation
3372 + virtual const char *GetEnv(const char *name) = 0;
3373 + virtual bool ParseAuth(const char *jobserver) = 0;
3374 + virtual bool AcquireToken() = 0;
3375 + virtual bool ReturnToken() = 0;
3383 diff --git a/src/tokenpool-none.cc b/src/tokenpool-none.cc
3384 index 4c592875b4..613d16882d 100644
3385 --- a/src/tokenpool-none.cc
3386 +++ b/src/tokenpool-none.cc
3390 // No-op TokenPool implementation
3391 -struct TokenPool *TokenPool::Get(bool ignore,
3393 - double& max_load_average) {
3394 +struct TokenPool *TokenPool::Get() {
3397 diff --git a/src/tokenpool.h b/src/tokenpool.h
3398 index 4bf477f20c..1be8e1d5ce 100644
3399 --- a/src/tokenpool.h
3400 +++ b/src/tokenpool.h
3402 -// Copyright 2016-2017 Google Inc. All Rights Reserved.
3403 +// Copyright 2016-2018 Google Inc. All Rights Reserved.
3405 // Licensed under the Apache License, Version 2.0 (the "License");
3406 // you may not use this file except in compliance with the License.
3408 // See the License for the specific language governing permissions and
3409 // limitations under the License.
3412 +#include <windows.h>
3415 // interface to token pool
3417 virtual ~TokenPool() {}
3418 @@ -21,14 +25,18 @@ struct TokenPool {
3419 virtual void Release() = 0;
3420 virtual void Clear() = 0;
3422 + // returns false if token pool setup failed
3423 + virtual bool Setup(bool ignore, bool verbose, double& max_load_average) = 0;
3427 + virtual void WaitForTokenAvailability(HANDLE ioport) = 0;
3428 + // returns true if a token has become available
3429 + // key is result from GetQueuedCompletionStatus()
3430 + virtual bool TokenIsAvailable(ULONG_PTR key) = 0;
3432 virtual int GetMonitorFd() = 0;
3435 // returns NULL if token pool is not available
3436 - static struct TokenPool *Get(bool ignore,
3438 - double& max_load_average);
3439 + static struct TokenPool *Get();
3441 diff --git a/src/tokenpool_test.cc b/src/tokenpool_test.cc
3442 index 6c89064ca4..8d4fd7d33a 100644
3443 --- a/src/tokenpool_test.cc
3444 +++ b/src/tokenpool_test.cc
3451 +#include <windows.h>
3453 +#include <unistd.h>
3458 -#include <unistd.h>
3461 +// should contain all valid characters
3462 +#define SEMAPHORE_NAME "abcdefghijklmnopqrstwxyz01234567890_"
3463 +#define AUTH_FORMAT(tmpl) "foo " tmpl "=%s bar"
3464 +#define ENVIRONMENT_CLEAR() SetEnvironmentVariable("MAKEFLAGS", NULL)
3465 +#define ENVIRONMENT_INIT(v) SetEnvironmentVariable("MAKEFLAGS", v)
3467 +#define AUTH_FORMAT(tmpl) "foo " tmpl "=%d,%d bar"
3468 #define ENVIRONMENT_CLEAR() unsetenv("MAKEFLAGS")
3469 -#define ENVIRONMENT_INIT(v) setenv("MAKEFLAGS", v, true);
3470 +#define ENVIRONMENT_INIT(v) setenv("MAKEFLAGS", v, true)
3474 @@ -32,43 +44,60 @@ const double kLoadAverageDefault = -1.23456789;
3475 struct TokenPoolTest : public testing::Test {
3481 + const char *semaphore_name_;
3482 + HANDLE semaphore_;
3487 virtual void SetUp() {
3488 load_avg_ = kLoadAverageDefault;
3491 ENVIRONMENT_CLEAR();
3493 + semaphore_name_ = SEMAPHORE_NAME;
3494 + if ((semaphore_ = CreateSemaphore(0, 0, 2, SEMAPHORE_NAME)) == NULL)
3497 - ASSERT_TRUE(false);
3499 + ASSERT_TRUE(false);
3502 - void CreatePool(const char *format, bool ignore_jobserver) {
3504 + void CreatePool(const char *format, bool ignore_jobserver = false) {
3506 - sprintf(buf_, format, fds_[0], fds_[1]);
3507 + sprintf(buf_, format,
3514 ENVIRONMENT_INIT(buf_);
3517 - tokens_ = TokenPool::Get(ignore_jobserver, false, load_avg_);
3518 + if ((tokens_ = TokenPool::Get()) != NULL) {
3519 + if (!tokens_->Setup(ignore_jobserver, false, load_avg_)) {
3526 void CreateDefaultPool() {
3527 - CreatePool("foo --jobserver-auth=%d,%d bar", false);
3528 + CreatePool(AUTH_FORMAT("--jobserver-auth"));
3531 virtual void TearDown() {
3536 + CloseHandle(semaphore_);
3540 - ENVIRONMENT_CLEAR();
3542 + ENVIRONMENT_CLEAR();
3546 @@ -82,10 +111,9 @@ TEST_F(TokenPoolTest, NoTokenPool) {
3547 EXPECT_EQ(kLoadAverageDefault, load_avg_);
3551 TEST_F(TokenPoolTest, SuccessfulOldSetup) {
3553 - CreatePool("foo --jobserver-fds=%d,%d bar", false);
3554 + CreatePool(AUTH_FORMAT("--jobserver-fds"));
3556 EXPECT_NE(NULL, tokens_);
3557 EXPECT_EQ(kLoadAverageDefault, load_avg_);
3558 @@ -100,19 +128,37 @@ TEST_F(TokenPoolTest, SuccessfulNewSetup) {
3561 TEST_F(TokenPoolTest, IgnoreWithJN) {
3562 - CreatePool("foo --jobserver-auth=%d,%d bar", true);
3563 + CreatePool(AUTH_FORMAT("--jobserver-auth"), true);
3565 EXPECT_EQ(NULL, tokens_);
3566 EXPECT_EQ(kLoadAverageDefault, load_avg_);
3569 TEST_F(TokenPoolTest, HonorLN) {
3570 - CreatePool("foo -l9 --jobserver-auth=%d,%d bar", false);
3571 + CreatePool(AUTH_FORMAT("-l9 --jobserver-auth"));
3573 EXPECT_NE(NULL, tokens_);
3574 EXPECT_EQ(9.0, load_avg_);
3578 +TEST_F(TokenPoolTest, SemaphoreNotFound) {
3579 + semaphore_name_ = SEMAPHORE_NAME "_foobar";
3580 + CreateDefaultPool();
3582 + EXPECT_EQ(NULL, tokens_);
3583 + EXPECT_EQ(kLoadAverageDefault, load_avg_);
3586 +TEST_F(TokenPoolTest, TokenIsAvailable) {
3587 + CreateDefaultPool();
3589 + ASSERT_NE(NULL, tokens_);
3590 + EXPECT_EQ(kLoadAverageDefault, load_avg_);
3592 + EXPECT_TRUE(tokens_->TokenIsAvailable((ULONG_PTR)tokens_));
3595 TEST_F(TokenPoolTest, MonitorFD) {
3596 CreateDefaultPool();
3598 @@ -121,6 +167,7 @@ TEST_F(TokenPoolTest, MonitorFD) {
3600 EXPECT_EQ(fds_[0], tokens_->GetMonitorFd());
3604 TEST_F(TokenPoolTest, ImplicitToken) {
3605 CreateDefaultPool();
3606 @@ -147,7 +194,13 @@ TEST_F(TokenPoolTest, TwoTokens) {
3607 EXPECT_FALSE(tokens_->Acquire());
3609 // jobserver offers 2nd token
3612 + ASSERT_TRUE(ReleaseSemaphore(semaphore_, 1, &previous));
3613 + ASSERT_EQ(0, previous);
3615 ASSERT_EQ(1u, write(fds_[1], "T", 1));
3617 EXPECT_TRUE(tokens_->Acquire());
3619 EXPECT_FALSE(tokens_->Acquire());
3620 @@ -160,8 +213,14 @@ TEST_F(TokenPoolTest, TwoTokens) {
3622 EXPECT_TRUE(tokens_->Acquire());
3624 - // there must be one token in the pipe
3625 + // there must be one token available
3627 + EXPECT_EQ(WAIT_OBJECT_0, WaitForSingleObject(semaphore_, 0));
3628 + EXPECT_TRUE(ReleaseSemaphore(semaphore_, 1, &previous));
3629 + EXPECT_EQ(0, previous);
3631 EXPECT_EQ(1u, read(fds_[0], buf_, sizeof(buf_)));
3635 EXPECT_TRUE(tokens_->Acquire());
3636 @@ -179,7 +238,13 @@ TEST_F(TokenPoolTest, Clear) {
3637 EXPECT_FALSE(tokens_->Acquire());
3639 // jobserver offers 2nd & 3rd token
3642 + ASSERT_TRUE(ReleaseSemaphore(semaphore_, 2, &previous));
3643 + ASSERT_EQ(0, previous);
3645 ASSERT_EQ(2u, write(fds_[1], "TT", 2));
3647 EXPECT_TRUE(tokens_->Acquire());
3649 EXPECT_TRUE(tokens_->Acquire());
3650 @@ -189,10 +254,16 @@ TEST_F(TokenPoolTest, Clear) {
3652 EXPECT_TRUE(tokens_->Acquire());
3654 - // there must be two tokens in the pipe
3655 + // there must be two tokens available
3657 + EXPECT_EQ(WAIT_OBJECT_0, WaitForSingleObject(semaphore_, 0));
3658 + EXPECT_EQ(WAIT_OBJECT_0, WaitForSingleObject(semaphore_, 0));
3659 + EXPECT_TRUE(ReleaseSemaphore(semaphore_, 2, &previous));
3660 + EXPECT_EQ(0, previous);
3662 EXPECT_EQ(2u, read(fds_[0], buf_, sizeof(buf_)));
3666 EXPECT_TRUE(tokens_->Acquire());
3670 From 2b9c81c0ec1226d8795e7725529f13be41eaa385 Mon Sep 17 00:00:00 2001
3671 From: Stefan Becker <chemobejk@gmail.com>
3672 Date: Fri, 14 Dec 2018 13:27:11 +0200
3673 Subject: [PATCH 11/11] Prepare PR for merging - part II
3675 - remove unnecessary "struct" from TokenPool
3676 - add PAPCFUNC cast to QueryUserAPC()
3677 - remove hard-coded MAKEFLAGS string from win32
3678 - remove useless build test CompleteNoWork
3679 - rename TokenPoolTest to TestTokenPool
3680 - add tokenpool modules to CMake build
3681 - remove unused no-op TokenPool implementation
3682 - fix errors flagged by codespell & clang-tidy
3683 - address review comments from
3685 https://github.com/ninja-build/ninja/pull/1140#pullrequestreview-195195803
3686 https://github.com/ninja-build/ninja/pull/1140#pullrequestreview-185089255
3687 https://github.com/ninja-build/ninja/pull/1140#issuecomment-473898963
3688 https://github.com/ninja-build/ninja/pull/1140#issuecomment-596624610
3690 CMakeLists.txt | 8 ++++-
3692 src/build_test.cc | 12 +------
3693 src/subprocess-posix.cc | 4 +--
3694 src/subprocess-win32.cc | 2 +-
3695 src/subprocess.h | 2 +-
3696 src/subprocess_test.cc | 26 +++++++-------
3697 src/tokenpool-gnu-make-posix.cc | 21 +++++------
3698 src/tokenpool-gnu-make-win32.cc | 36 ++++++++++---------
3699 src/tokenpool-gnu-make.cc | 63 +++++++++++++++++----------------
3700 src/tokenpool-gnu-make.h | 6 ++--
3701 src/tokenpool-none.cc | 22 ------------
3702 src/tokenpool.h | 2 +-
3703 src/tokenpool_test.cc | 8 ++---
3704 14 files changed, 94 insertions(+), 120 deletions(-)
3705 delete mode 100644 src/tokenpool-none.cc
3707 diff --git a/CMakeLists.txt b/CMakeLists.txt
3708 index 57ae548f5b..e2876fe413 100644
3709 --- a/CMakeLists.txt
3710 +++ b/CMakeLists.txt
3711 @@ -112,6 +112,7 @@ add_library(libninja OBJECT
3714 src/string_piece_util.cc
3715 + src/tokenpool-gnu-make.cc
3719 @@ -123,9 +124,13 @@ if(WIN32)
3720 src/msvc_helper_main-win32.cc
3722 src/minidump-win32.cc
3723 + src/tokenpool-gnu-make-win32.cc
3726 - target_sources(libninja PRIVATE src/subprocess-posix.cc)
3727 + target_sources(libninja PRIVATE
3728 + src/subprocess-posix.cc
3729 + src/tokenpool-gnu-make-posix.cc
3731 if(CMAKE_SYSTEM_NAME STREQUAL "OS400" OR CMAKE_SYSTEM_NAME STREQUAL "AIX")
3732 target_sources(libninja PRIVATE src/getopt.c)
3734 @@ -204,6 +209,7 @@ if(BUILD_TESTING)
3735 src/string_piece_util_test.cc
3736 src/subprocess_test.cc
3738 + src/tokenpool_test.cc
3742 diff --git a/src/build.cc b/src/build.cc
3743 index 20c3bdc2a0..854df08c2a 100644
3746 @@ -467,7 +467,7 @@ struct RealCommandRunner : public CommandRunner {
3747 // copy of config_.max_load_average; can be modified by TokenPool setup
3748 double max_load_average_;
3749 SubprocessSet subprocs_;
3750 - TokenPool *tokens_;
3751 + TokenPool* tokens_;
3752 map<const Subprocess*, Edge*> subproc_to_edge_;
3755 diff --git a/src/build_test.cc b/src/build_test.cc
3756 index dd41dfbe1d..8901c9518f 100644
3757 --- a/src/build_test.cc
3758 +++ b/src/build_test.cc
3759 @@ -4098,7 +4098,7 @@ struct BuildTokenTest : public BuildTest {
3760 void ExpectWaitForCommand(int count, ...);
3763 - void EnqueueBooleans(vector<bool>& booleans, int count, va_list ao);
3764 + void EnqueueBooleans(vector<bool>& booleans, int count, va_list ap);
3767 void BuildTokenTest::SetUp() {
3768 @@ -4144,16 +4144,6 @@ void BuildTokenTest::EnqueueBooleans(vector<bool>& booleans, int count, va_list
3772 -TEST_F(BuildTokenTest, CompleteNoWork) {
3773 - // plan should not execute anything
3776 - EXPECT_TRUE(builder_.Build(&err));
3777 - EXPECT_EQ("", err);
3779 - EXPECT_EQ(0u, token_command_runner_.commands_ran_.size());
3782 TEST_F(BuildTokenTest, DoNotAquireToken) {
3783 // plan should execute one command
3785 diff --git a/src/subprocess-posix.cc b/src/subprocess-posix.cc
3786 index 74451b0be2..31839276c4 100644
3787 --- a/src/subprocess-posix.cc
3788 +++ b/src/subprocess-posix.cc
3789 @@ -250,7 +250,7 @@ Subprocess *SubprocessSet::Add(const string& command, bool use_console) {
3793 -bool SubprocessSet::DoWork(struct TokenPool* tokens) {
3794 +bool SubprocessSet::DoWork(TokenPool* tokens) {
3798 @@ -315,7 +315,7 @@ bool SubprocessSet::DoWork(struct TokenPool* tokens) {
3801 #else // !defined(USE_PPOLL)
3802 -bool SubprocessSet::DoWork(struct TokenPool* tokens) {
3803 +bool SubprocessSet::DoWork(TokenPool* tokens) {
3807 diff --git a/src/subprocess-win32.cc b/src/subprocess-win32.cc
3808 index ce3e2c20a4..2926e9a221 100644
3809 --- a/src/subprocess-win32.cc
3810 +++ b/src/subprocess-win32.cc
3811 @@ -252,7 +252,7 @@ Subprocess *SubprocessSet::Add(const string& command, bool use_console) {
3815 -bool SubprocessSet::DoWork(struct TokenPool* tokens) {
3816 +bool SubprocessSet::DoWork(TokenPool* tokens) {
3818 Subprocess* subproc;
3819 OVERLAPPED* overlapped;
3820 diff --git a/src/subprocess.h b/src/subprocess.h
3821 index 9ea67ea477..1ec78171e8 100644
3822 --- a/src/subprocess.h
3823 +++ b/src/subprocess.h
3824 @@ -86,7 +86,7 @@ struct SubprocessSet {
3827 Subprocess* Add(const std::string& command, bool use_console = false);
3828 - bool DoWork(struct TokenPool* tokens);
3829 + bool DoWork(TokenPool* tokens);
3830 Subprocess* NextFinished();
3833 diff --git a/src/subprocess_test.cc b/src/subprocess_test.cc
3834 index f625963462..7b146f31be 100644
3835 --- a/src/subprocess_test.cc
3836 +++ b/src/subprocess_test.cc
3837 @@ -35,7 +35,7 @@ const char* kSimpleCommand = "cmd /c dir \\";
3838 const char* kSimpleCommand = "ls /";
3841 -struct TokenPoolTest : public TokenPool {
3842 +struct TestTokenPool : public TokenPool {
3843 bool Acquire() { return false; }
3846 @@ -58,7 +58,7 @@ struct TokenPoolTest : public TokenPool {
3848 struct SubprocessTest : public testing::Test {
3849 SubprocessSet subprocs_;
3850 - TokenPoolTest tokens_;
3851 + TestTokenPool tokens_;
3854 } // anonymous namespace
3855 @@ -73,7 +73,7 @@ TEST_F(SubprocessTest, BadCommandStderr) {
3856 // Pretend we discovered that stderr was ready for writing.
3857 subprocs_.DoWork(NULL);
3859 - ASSERT_EQ(false, subprocs_.IsTokenAvailable());
3860 + ASSERT_FALSE(subprocs_.IsTokenAvailable());
3862 EXPECT_EQ(ExitFailure, subproc->Finish());
3863 EXPECT_NE("", subproc->GetOutput());
3864 @@ -89,7 +89,7 @@ TEST_F(SubprocessTest, NoSuchCommand) {
3865 // Pretend we discovered that stderr was ready for writing.
3866 subprocs_.DoWork(NULL);
3868 - ASSERT_EQ(false, subprocs_.IsTokenAvailable());
3869 + ASSERT_FALSE(subprocs_.IsTokenAvailable());
3871 EXPECT_EQ(ExitFailure, subproc->Finish());
3872 EXPECT_NE("", subproc->GetOutput());
3873 @@ -109,7 +109,7 @@ TEST_F(SubprocessTest, InterruptChild) {
3874 while (!subproc->Done()) {
3875 subprocs_.DoWork(NULL);
3877 - ASSERT_EQ(false, subprocs_.IsTokenAvailable());
3878 + ASSERT_FALSE(subprocs_.IsTokenAvailable());
3880 EXPECT_EQ(ExitInterrupted, subproc->Finish());
3882 @@ -135,7 +135,7 @@ TEST_F(SubprocessTest, InterruptChildWithSigTerm) {
3883 while (!subproc->Done()) {
3884 subprocs_.DoWork(NULL);
3886 - ASSERT_EQ(false, subprocs_.IsTokenAvailable());
3887 + ASSERT_FALSE(subprocs_.IsTokenAvailable());
3889 EXPECT_EQ(ExitInterrupted, subproc->Finish());
3891 @@ -161,7 +161,7 @@ TEST_F(SubprocessTest, InterruptChildWithSigHup) {
3892 while (!subproc->Done()) {
3893 subprocs_.DoWork(NULL);
3895 - ASSERT_EQ(false, subprocs_.IsTokenAvailable());
3896 + ASSERT_FALSE(subprocs_.IsTokenAvailable());
3898 EXPECT_EQ(ExitInterrupted, subproc->Finish());
3900 @@ -190,7 +190,7 @@ TEST_F(SubprocessTest, Console) {
3901 while (!subproc->Done()) {
3902 subprocs_.DoWork(NULL);
3904 - ASSERT_EQ(false, subprocs_.IsTokenAvailable());
3905 + ASSERT_FALSE(subprocs_.IsTokenAvailable());
3907 EXPECT_EQ(ExitSuccess, subproc->Finish());
3909 @@ -206,7 +206,7 @@ TEST_F(SubprocessTest, SetWithSingle) {
3910 while (!subproc->Done()) {
3911 subprocs_.DoWork(NULL);
3913 - ASSERT_EQ(false, subprocs_.IsTokenAvailable());
3914 + ASSERT_FALSE(subprocs_.IsTokenAvailable());
3915 ASSERT_EQ(ExitSuccess, subproc->Finish());
3916 ASSERT_NE("", subproc->GetOutput());
3918 @@ -243,7 +243,7 @@ TEST_F(SubprocessTest, SetWithMulti) {
3919 ASSERT_GT(subprocs_.running_.size(), 0u);
3920 subprocs_.DoWork(NULL);
3922 - ASSERT_EQ(false, subprocs_.IsTokenAvailable());
3923 + ASSERT_FALSE(subprocs_.IsTokenAvailable());
3924 ASSERT_EQ(0u, subprocs_.running_.size());
3925 ASSERT_EQ(3u, subprocs_.finished_.size());
3927 @@ -278,7 +278,7 @@ TEST_F(SubprocessTest, SetWithLots) {
3928 subprocs_.ResetTokenAvailable();
3929 while (!subprocs_.running_.empty())
3930 subprocs_.DoWork(NULL);
3931 - ASSERT_EQ(false, subprocs_.IsTokenAvailable());
3932 + ASSERT_FALSE(subprocs_.IsTokenAvailable());
3933 for (size_t i = 0; i < procs.size(); ++i) {
3934 ASSERT_EQ(ExitSuccess, procs[i]->Finish());
3935 ASSERT_NE("", procs[i]->GetOutput());
3936 @@ -298,7 +298,7 @@ TEST_F(SubprocessTest, ReadStdin) {
3937 while (!subproc->Done()) {
3938 subprocs_.DoWork(NULL);
3940 - ASSERT_EQ(false, subprocs_.IsTokenAvailable());
3941 + ASSERT_FALSE(subprocs_.IsTokenAvailable());
3942 ASSERT_EQ(ExitSuccess, subproc->Finish());
3943 ASSERT_EQ(1u, subprocs_.finished_.size());
3945 @@ -322,7 +322,7 @@ TEST_F(SubprocessTest, TokenAvailable) {
3946 subprocs_.DoWork(&tokens_);
3948 tokens_._token_available = false;
3949 - // we need to loop here as we have no conrol where the token
3950 + // we need to loop here as we have no control where the token
3951 // I/O completion post ends up in the queue
3952 while (!subproc->Done() && !subprocs_.IsTokenAvailable()) {
3953 subprocs_.DoWork(&tokens_);
3954 diff --git a/src/tokenpool-gnu-make-posix.cc b/src/tokenpool-gnu-make-posix.cc
3955 index 70d84bfff7..353bda226a 100644
3956 --- a/src/tokenpool-gnu-make-posix.cc
3957 +++ b/src/tokenpool-gnu-make-posix.cc
3958 @@ -32,8 +32,8 @@ struct GNUmakeTokenPoolPosix : public GNUmakeTokenPool {
3960 virtual int GetMonitorFd();
3962 - virtual const char *GetEnv(const char *name) { return getenv(name); };
3963 - virtual bool ParseAuth(const char *jobserver);
3964 + virtual const char* GetEnv(const char* name) { return getenv(name); };
3965 + virtual bool ParseAuth(const char* jobserver);
3966 virtual bool AcquireToken();
3967 virtual bool ReturnToken();
3969 @@ -64,9 +64,7 @@ bool GNUmakeTokenPoolPosix::CheckFd(int fd) {
3972 int ret = fcntl(fd, F_GETFD);
3979 int GNUmakeTokenPoolPosix::dup_rfd_ = -1;
3980 @@ -82,14 +80,13 @@ bool GNUmakeTokenPoolPosix::SetAlarmHandler() {
3981 act.sa_handler = CloseDupRfd;
3982 if (sigaction(SIGALRM, &act, &old_act_) < 0) {
3983 perror("sigaction:");
3994 -bool GNUmakeTokenPoolPosix::ParseAuth(const char *jobserver) {
3995 +bool GNUmakeTokenPoolPosix::ParseAuth(const char* jobserver) {
3998 if ((sscanf(jobserver, "%*[^=]=%d,%d", &rfd, &wfd) == 2) &&
3999 @@ -195,9 +192,9 @@ bool GNUmakeTokenPoolPosix::ReturnToken() {
4002 int GNUmakeTokenPoolPosix::GetMonitorFd() {
4007 -struct TokenPool *TokenPool::Get() {
4008 +TokenPool* TokenPool::Get() {
4009 return new GNUmakeTokenPoolPosix;
4011 diff --git a/src/tokenpool-gnu-make-win32.cc b/src/tokenpool-gnu-make-win32.cc
4012 index 2719f2c1fc..b2bb52fadb 100644
4013 --- a/src/tokenpool-gnu-make-win32.cc
4014 +++ b/src/tokenpool-gnu-make-win32.cc
4017 #include "tokenpool-gnu-make.h"
4019 -// always include first to make sure other headers do the correct thing...
4020 +// Always include this first.
4021 +// Otherwise the other system headers don't work correctly under Win32
4022 #include <windows.h>
4025 @@ -32,8 +33,8 @@ struct GNUmakeTokenPoolWin32 : public GNUmakeTokenPool {
4026 virtual void WaitForTokenAvailability(HANDLE ioport);
4027 virtual bool TokenIsAvailable(ULONG_PTR key);
4029 - virtual const char *GetEnv(const char *name);
4030 - virtual bool ParseAuth(const char *jobserver);
4031 + virtual const char* GetEnv(const char* name);
4032 + virtual bool ParseAuth(const char* jobserver);
4033 virtual bool AcquireToken();
4034 virtual bool ReturnToken();
4036 @@ -98,19 +99,19 @@ GNUmakeTokenPoolWin32::~GNUmakeTokenPoolWin32() {
4040 -const char *GNUmakeTokenPoolWin32::GetEnv(const char *name) {
4041 +const char* GNUmakeTokenPoolWin32::GetEnv(const char* name) {
4042 // getenv() does not work correctly together with tokenpool_tests.cc
4043 static char buffer[MAX_PATH + 1];
4044 - if (GetEnvironmentVariable("MAKEFLAGS", buffer, sizeof(buffer)) == 0)
4045 + if (GetEnvironmentVariable(name, buffer, sizeof(buffer)) == 0)
4051 -bool GNUmakeTokenPoolWin32::ParseAuth(const char *jobserver) {
4052 +bool GNUmakeTokenPoolWin32::ParseAuth(const char* jobserver) {
4053 // match "--jobserver-auth=gmake_semaphore_<INTEGER>..."
4054 - const char *start = strchr(jobserver, '=');
4055 + const char* start = strchr(jobserver, '=');
4057 - const char *end = start;
4058 + const char* end = start;
4062 @@ -119,14 +120,15 @@ bool GNUmakeTokenPoolWin32::ParseAuth(const char *jobserver) {
4064 len = end - start; // includes string terminator in count
4066 - if ((len > 1) && ((auth = (char *)malloc(len)) != NULL)) {
4067 + if ((len > 1) && ((auth = (char*)malloc(len)) != NULL)) {
4068 strncpy(auth, start + 1, len - 1);
4069 auth[len - 1] = '\0';
4071 - if ((semaphore_jobserver_ = OpenSemaphore(SEMAPHORE_ALL_ACCESS, /* Semaphore access setting */
4072 - FALSE, /* Child processes DON'T inherit */
4073 - auth /* Semaphore name */
4075 + if ((semaphore_jobserver_ =
4076 + OpenSemaphore(SEMAPHORE_ALL_ACCESS, /* Semaphore access setting */
4077 + FALSE, /* Child processes DON'T inherit */
4078 + auth /* Semaphore name */
4083 @@ -171,7 +173,7 @@ DWORD GNUmakeTokenPoolWin32::SemaphoreThread() {
4086 DWORD WINAPI GNUmakeTokenPoolWin32::SemaphoreThreadWrapper(LPVOID param) {
4087 - GNUmakeTokenPoolWin32 *This = (GNUmakeTokenPoolWin32 *) param;
4088 + GNUmakeTokenPoolWin32* This = (GNUmakeTokenPoolWin32*) param;
4089 return This->SemaphoreThread();
4092 @@ -216,7 +218,7 @@ void GNUmakeTokenPoolWin32::WaitForTokenAvailability(HANDLE ioport) {
4094 bool GNUmakeTokenPoolWin32::TokenIsAvailable(ULONG_PTR key) {
4095 // alert child thread to break wait on token semaphore
4096 - QueueUserAPC(&NoopAPCFunc, child_, (ULONG_PTR)NULL);
4097 + QueueUserAPC((PAPCFUNC)&NoopAPCFunc, child_, (ULONG_PTR)NULL);
4099 // return true when GetQueuedCompletionStatus() returned our key
4100 return key == (ULONG_PTR) this;
4101 @@ -232,6 +234,6 @@ void GNUmakeTokenPoolWin32::WaitForObject(HANDLE object) {
4102 Win32Fatal("WaitForSingleObject");
4105 -struct TokenPool *TokenPool::Get() {
4106 +TokenPool* TokenPool::Get() {
4107 return new GNUmakeTokenPoolWin32;
4109 diff --git a/src/tokenpool-gnu-make.cc b/src/tokenpool-gnu-make.cc
4110 index 92ff611721..60e0552924 100644
4111 --- a/src/tokenpool-gnu-make.cc
4112 +++ b/src/tokenpool-gnu-make.cc
4113 @@ -31,36 +31,37 @@ GNUmakeTokenPool::~GNUmakeTokenPool() {
4114 bool GNUmakeTokenPool::Setup(bool ignore,
4116 double& max_load_average) {
4117 - const char *value = GetEnv("MAKEFLAGS");
4119 - // GNU make <= 4.1
4120 - const char *jobserver = strstr(value, "--jobserver-fds=");
4121 + const char* value = GetEnv("MAKEFLAGS");
4125 + // GNU make <= 4.1
4126 + const char* jobserver = strstr(value, "--jobserver-fds=");
4130 - jobserver = strstr(value, "--jobserver-auth=");
4132 - LinePrinter printer;
4135 - printer.PrintOnNewLine("ninja: warning: -jN forced on command line; ignoring GNU make jobserver.\n");
4137 - if (ParseAuth(jobserver)) {
4138 - const char *l_arg = strstr(value, " -l");
4139 - int load_limit = -1;
4142 - printer.PrintOnNewLine("ninja: using GNU make jobserver.\n");
4145 - // translate GNU make -lN to ninja -lN
4147 - (sscanf(l_arg + 3, "%d ", &load_limit) == 1) &&
4148 - (load_limit > 0)) {
4149 - max_load_average = load_limit;
4153 + jobserver = strstr(value, "--jobserver-auth=");
4155 + LinePrinter printer;
4158 + printer.PrintOnNewLine("ninja: warning: -jN forced on command line; ignoring GNU make jobserver.\n");
4160 + if (ParseAuth(jobserver)) {
4161 + const char* l_arg = strstr(value, " -l");
4162 + int load_limit = -1;
4165 + printer.PrintOnNewLine("ninja: using GNU make jobserver.\n");
4168 + // translate GNU make -lN to ninja -lN
4170 + (sscanf(l_arg + 3, "%d ", &load_limit) == 1) &&
4171 + (load_limit > 0)) {
4172 + max_load_average = load_limit;
4179 @@ -76,10 +77,10 @@ bool GNUmakeTokenPool::Acquire() {
4184 - // no token available
4188 + // no token available
4192 void GNUmakeTokenPool::Reserve() {
4193 diff --git a/src/tokenpool-gnu-make.h b/src/tokenpool-gnu-make.h
4194 index d3852088e2..c94cca5e2d 100644
4195 --- a/src/tokenpool-gnu-make.h
4196 +++ b/src/tokenpool-gnu-make.h
4198 // interface to GNU make token pool
4199 struct GNUmakeTokenPool : public TokenPool {
4201 - virtual ~GNUmakeTokenPool();
4202 + ~GNUmakeTokenPool();
4204 // token pool implementation
4205 virtual bool Acquire();
4206 @@ -27,8 +27,8 @@ struct GNUmakeTokenPool : public TokenPool {
4207 virtual bool Setup(bool ignore, bool verbose, double& max_load_average);
4209 // platform specific implementation
4210 - virtual const char *GetEnv(const char *name) = 0;
4211 - virtual bool ParseAuth(const char *jobserver) = 0;
4212 + virtual const char* GetEnv(const char* name) = 0;
4213 + virtual bool ParseAuth(const char* jobserver) = 0;
4214 virtual bool AcquireToken() = 0;
4215 virtual bool ReturnToken() = 0;
4217 diff --git a/src/tokenpool-none.cc b/src/tokenpool-none.cc
4218 deleted file mode 100644
4219 index 613d16882d..0000000000
4220 --- a/src/tokenpool-none.cc
4223 -// Copyright 2016-2018 Google Inc. All Rights Reserved.
4225 -// Licensed under the Apache License, Version 2.0 (the "License");
4226 -// you may not use this file except in compliance with the License.
4227 -// You may obtain a copy of the License at
4229 -// http://www.apache.org/licenses/LICENSE-2.0
4231 -// Unless required by applicable law or agreed to in writing, software
4232 -// distributed under the License is distributed on an "AS IS" BASIS,
4233 -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4234 -// See the License for the specific language governing permissions and
4235 -// limitations under the License.
4237 -#include "tokenpool.h"
4239 -#include <stdlib.h>
4241 -// No-op TokenPool implementation
4242 -struct TokenPool *TokenPool::Get() {
4245 diff --git a/src/tokenpool.h b/src/tokenpool.h
4246 index 1be8e1d5ce..931c22754d 100644
4247 --- a/src/tokenpool.h
4248 +++ b/src/tokenpool.h
4249 @@ -38,5 +38,5 @@ struct TokenPool {
4252 // returns NULL if token pool is not available
4253 - static struct TokenPool *Get();
4254 + static TokenPool* Get();
4256 diff --git a/src/tokenpool_test.cc b/src/tokenpool_test.cc
4257 index 8d4fd7d33a..8d3061cb30 100644
4258 --- a/src/tokenpool_test.cc
4259 +++ b/src/tokenpool_test.cc
4260 @@ -43,10 +43,10 @@ const double kLoadAverageDefault = -1.23456789;
4262 struct TokenPoolTest : public testing::Test {
4264 - TokenPool *tokens_;
4265 + TokenPool* tokens_;
4268 - const char *semaphore_name_;
4269 + const char* semaphore_name_;
4273 @@ -65,7 +65,7 @@ struct TokenPoolTest : public testing::Test {
4277 - void CreatePool(const char *format, bool ignore_jobserver = false) {
4278 + void CreatePool(const char* format, bool ignore_jobserver = false) {
4280 sprintf(buf_, format,
4282 @@ -209,7 +209,7 @@ TEST_F(TokenPoolTest, TwoTokens) {
4284 EXPECT_TRUE(tokens_->Acquire());
4286 - // release implict token - must return 2nd token back to jobserver
4287 + // release implicit token - must return 2nd token back to jobserver
4289 EXPECT_TRUE(tokens_->Acquire());