7dac8ef8149c5fa2430f7768a95b0c467a1592a8
[openwrt/openwrt.git] / tools / ninja / patches / 100-make_jobserver_support.patch
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
5
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
12 changes as follows
13 * CanRunMore() only returns true if TokenPool::Acquire() returns true
14 * StartCommand() calls TokenPool::Reserve()
15 * WaitForCommand() calls TokenPool::Release()
16
17 Documentation for GNU make jobserver
18
19 http://make.mad-scientist.net/papers/jobserver-implementation/
20
21 Fixes https://github.com/ninja-build/ninja/issues/1139
22 ---
23 configure.py | 2 +
24 src/build.cc | 63 ++++++++----
25 src/build.h | 3 +
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
33
34 diff --git a/configure.py b/configure.py
35 index 43904349a8..db3492c93c 100755
36 --- a/configure.py
37 +++ b/configure.py
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',
42 + 'tokenpool-none',
43 'includes_normalize-win32',
44 'msvc_helper-win32',
45 'msvc_helper_main-win32']:
46 @@ -531,6 +532,7 @@ def has_re2c():
47 objs += cc('getopt')
48 else:
49 objs += cxx('subprocess-posix')
50 + objs += cxx('tokenpool-gnu-make')
51 if platform.is_aix():
52 objs += cc('getopt')
53 if platform.is_msvc():
54 diff --git a/src/build.cc b/src/build.cc
55 index 6f11ed7a3c..fa096eac33 100644
56 --- a/src/build.cc
57 +++ b/src/build.cc
58 @@ -35,6 +35,7 @@
59 #include "state.h"
60 #include "status.h"
61 #include "subprocess.h"
62 +#include "tokenpool.h"
63 #include "util.h"
64
65 using namespace std;
66 @@ -149,7 +150,7 @@ void Plan::EdgeWanted(const Edge* edge) {
67 }
68
69 Edge* Plan::FindWork() {
70 - if (ready_.empty())
71 + if (!more_ready())
72 return NULL;
73 EdgeSet::iterator e = ready_.begin();
74 Edge* edge = *e;
75 @@ -448,8 +449,8 @@ void Plan::Dump() const {
76 }
77
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 {
87
88 const BuildConfig& config_;
89 SubprocessSet subprocs_;
90 + TokenPool *tokens_;
91 map<const Subprocess*, Edge*> subproc_to_edge_;
92 };
93
94 +RealCommandRunner::RealCommandRunner(const BuildConfig& config) : config_(config) {
95 + tokens_ = TokenPool::Get();
96 +}
97 +
98 +RealCommandRunner::~RealCommandRunner() {
99 + delete tokens_;
100 +}
101 +
102 vector<Edge*> RealCommandRunner::GetActiveEdges() {
103 vector<Edge*> edges;
104 for (map<const Subprocess*, Edge*>::iterator e = subproc_to_edge_.begin();
105 @@ -471,14 +481,18 @@ vector<Edge*> RealCommandRunner::GetActiveEdges() {
106
107 void RealCommandRunner::Abort() {
108 subprocs_.Clear();
109 + if (tokens_)
110 + tokens_->Clear();
111 }
112
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())));
123 }
124
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());
128 if (!subproc)
129 return false;
130 + if (tokens_)
131 + tokens_->Reserve();
132 subproc_to_edge_.insert(make_pair(subproc, edge));
133
134 return true;
135 @@ -499,6 +515,9 @@ bool RealCommandRunner::WaitForCommand(Result* result) {
136 return false;
137 }
138
139 + if (tokens_)
140 + tokens_->Release();
141 +
142 result->status = subproc->Finish();
143 result->output = subproc->GetOutput();
144
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();
157 }
158
159 - if (!StartEdge(edge, err)) {
160 + if (!StartEdge(edge, err)) {
161 + Cleanup();
162 + status_->BuildFinished();
163 + return false;
164 + }
165 +
166 + if (edge->is_phony()) {
167 + if (!plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, err)) {
168 Cleanup();
169 status_->BuildFinished();
170 return false;
171 }
172 -
173 - if (edge->is_phony()) {
174 - if (!plan_.EdgeFinished(edge, Plan::kEdgeSucceeded, err)) {
175 - Cleanup();
176 - status_->BuildFinished();
177 - return false;
178 - }
179 - } else {
180 - ++pending_commands;
181 - }
182 -
183 - // We made some progress; go back to the main loop.
184 - continue;
185 + } else {
186 + ++pending_commands;
187 }
188 +
189 + // We made some progress; go back to the main loop.
190 + continue;
191 }
192
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
196 --- a/src/build.h
197 +++ b/src/build.h
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; }
201
202 + /// Returns true if there's more edges ready to start
203 + bool more_ready() const { return !ready_.empty(); }
204 +
205 /// Dumps the current state of the plan.
206 void Dump() const;
207
208 diff --git a/src/tokenpool-gnu-make.cc b/src/tokenpool-gnu-make.cc
209 new file mode 100644
210 index 0000000000..a8f9b7139d
211 --- /dev/null
212 +++ b/src/tokenpool-gnu-make.cc
213 @@ -0,0 +1,211 @@
214 +// Copyright 2016 Google Inc. All Rights Reserved.
215 +//
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
219 +//
220 +// http://www.apache.org/licenses/LICENSE-2.0
221 +//
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.
227 +
228 +#include "tokenpool.h"
229 +
230 +#include <errno.h>
231 +#include <fcntl.h>
232 +#include <poll.h>
233 +#include <unistd.h>
234 +#include <signal.h>
235 +#include <stdio.h>
236 +#include <string.h>
237 +#include <stdlib.h>
238 +
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();
244 +
245 + virtual bool Acquire();
246 + virtual void Reserve();
247 + virtual void Release();
248 + virtual void Clear();
249 +
250 + bool Setup();
251 +
252 + private:
253 + int available_;
254 + int used_;
255 +
256 +#ifdef _WIN32
257 + // @TODO
258 +#else
259 + int rfd_;
260 + int wfd_;
261 +
262 + struct sigaction old_act_;
263 + bool restore_;
264 +
265 + static int dup_rfd_;
266 + static void CloseDupRfd(int signum);
267 +
268 + bool CheckFd(int fd);
269 + bool SetAlarmHandler();
270 +#endif
271 +
272 + void Return();
273 +};
274 +
275 +// every instance owns an implicit token -> available_ == 1
276 +GNUmakeTokenPool::GNUmakeTokenPool() : available_(1), used_(0),
277 + rfd_(-1), wfd_(-1), restore_(false) {
278 +}
279 +
280 +GNUmakeTokenPool::~GNUmakeTokenPool() {
281 + Clear();
282 + if (restore_)
283 + sigaction(SIGALRM, &old_act_, NULL);
284 +}
285 +
286 +bool GNUmakeTokenPool::CheckFd(int fd) {
287 + if (fd < 0)
288 + return false;
289 + int ret = fcntl(fd, F_GETFD);
290 + if (ret < 0)
291 + return false;
292 + return true;
293 +}
294 +
295 +int GNUmakeTokenPool::dup_rfd_ = -1;
296 +
297 +void GNUmakeTokenPool::CloseDupRfd(int signum) {
298 + close(dup_rfd_);
299 + dup_rfd_ = -1;
300 +}
301 +
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:");
308 + return(false);
309 + } else {
310 + restore_ = true;
311 + return(true);
312 + }
313 +}
314 +
315 +bool GNUmakeTokenPool::Setup() {
316 + const char *value = getenv("MAKEFLAGS");
317 + if (value) {
318 + // GNU make <= 4.1
319 + const char *jobserver = strstr(value, "--jobserver-fds=");
320 + // GNU make => 4.2
321 + if (!jobserver)
322 + jobserver = strstr(value, "--jobserver-auth=");
323 + if (jobserver) {
324 + int rfd = -1;
325 + int wfd = -1;
326 + if ((sscanf(jobserver, "%*[^=]=%d,%d", &rfd, &wfd) == 2) &&
327 + CheckFd(rfd) &&
328 + CheckFd(wfd) &&
329 + SetAlarmHandler()) {
330 + printf("ninja: using GNU make jobserver.\n");
331 + rfd_ = rfd;
332 + wfd_ = wfd;
333 + return true;
334 + }
335 + }
336 + }
337 +
338 + return false;
339 +}
340 +
341 +bool GNUmakeTokenPool::Acquire() {
342 + if (available_ > 0)
343 + return true;
344 +
345 +#ifdef USE_PPOLL
346 + pollfd pollfds[] = {{rfd_, POLLIN, 0}};
347 + int ret = poll(pollfds, 1, 0);
348 +#else
349 + fd_set set;
350 + struct timeval timeout = { 0, 0 };
351 + FD_ZERO(&set);
352 + FD_SET(rfd_, &set);
353 + int ret = select(rfd_ + 1, &set, NULL, NULL, &timeout);
354 +#endif
355 + if (ret > 0) {
356 + dup_rfd_ = dup(rfd_);
357 +
358 + if (dup_rfd_ != -1) {
359 + struct sigaction act, old_act;
360 + int ret = 0;
361 +
362 + memset(&act, 0, sizeof(act));
363 + act.sa_handler = CloseDupRfd;
364 + if (sigaction(SIGCHLD, &act, &old_act) == 0) {
365 + char buf;
366 +
367 + // block until token read, child exits or timeout
368 + alarm(1);
369 + ret = read(dup_rfd_, &buf, 1);
370 + alarm(0);
371 +
372 + sigaction(SIGCHLD, &old_act, NULL);
373 + }
374 +
375 + CloseDupRfd(0);
376 +
377 + if (ret > 0) {
378 + available_++;
379 + return true;
380 + }
381 + }
382 + }
383 + return false;
384 +}
385 +
386 +void GNUmakeTokenPool::Reserve() {
387 + available_--;
388 + used_++;
389 +}
390 +
391 +void GNUmakeTokenPool::Return() {
392 + const char buf = '+';
393 + while (1) {
394 + int ret = write(wfd_, &buf, 1);
395 + if (ret > 0)
396 + available_--;
397 + if ((ret != -1) || (errno != EINTR))
398 + return;
399 + // write got interrupted - retry
400 + }
401 +}
402 +
403 +void GNUmakeTokenPool::Release() {
404 + available_++;
405 + used_--;
406 + if (available_ > 1)
407 + Return();
408 +}
409 +
410 +void GNUmakeTokenPool::Clear() {
411 + while (used_ > 0)
412 + Release();
413 + while (available_ > 1)
414 + Return();
415 +}
416 +
417 +struct TokenPool *TokenPool::Get(void) {
418 + GNUmakeTokenPool *tokenpool = new GNUmakeTokenPool;
419 + if (tokenpool->Setup())
420 + return tokenpool;
421 + else
422 + delete tokenpool;
423 + return NULL;
424 +}
425 diff --git a/src/tokenpool-none.cc b/src/tokenpool-none.cc
426 new file mode 100644
427 index 0000000000..602b3316f5
428 --- /dev/null
429 +++ b/src/tokenpool-none.cc
430 @@ -0,0 +1,27 @@
431 +// Copyright 2016 Google Inc. All Rights Reserved.
432 +//
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
436 +//
437 +// http://www.apache.org/licenses/LICENSE-2.0
438 +//
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.
444 +
445 +#include "tokenpool.h"
446 +
447 +#include <fcntl.h>
448 +#include <poll.h>
449 +#include <unistd.h>
450 +#include <stdio.h>
451 +#include <string.h>
452 +#include <stdlib.h>
453 +
454 +// No-op TokenPool implementation
455 +struct TokenPool *TokenPool::Get(void) {
456 + return NULL;
457 +}
458 diff --git a/src/tokenpool.h b/src/tokenpool.h
459 new file mode 100644
460 index 0000000000..f560b1083b
461 --- /dev/null
462 +++ b/src/tokenpool.h
463 @@ -0,0 +1,26 @@
464 +// Copyright 2016 Google Inc. All Rights Reserved.
465 +//
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
469 +//
470 +// http://www.apache.org/licenses/LICENSE-2.0
471 +//
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.
477 +
478 +// interface to token pool
479 +struct TokenPool {
480 + virtual ~TokenPool() {}
481 +
482 + virtual bool Acquire() = 0;
483 + virtual void Reserve() = 0;
484 + virtual void Release() = 0;
485 + virtual void Clear() = 0;
486 +
487 + // returns NULL if token pool is not available
488 + static struct TokenPool *Get(void);
489 +};
490
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()
495
496 Improve on the original jobserver client implementation. This makes
497 ninja a more aggressive GNU make jobserver client.
498
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
505 ---
506 src/build.cc | 53 +++++++++++++++++++++++++++++----------
507 src/build.h | 3 ++-
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(-)
517
518 diff --git a/src/build.cc b/src/build.cc
519 index fa096eac33..a25c349050 100644
520 --- a/src/build.cc
521 +++ b/src/build.cc
522 @@ -48,8 +48,9 @@ struct DryRunCommandRunner : public CommandRunner {
523
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);
530
531 private:
532 queue<Edge*> finished_;
533 @@ -59,12 +60,16 @@ bool DryRunCommandRunner::CanRunMore() const {
534 return true;
535 }
536
537 +bool DryRunCommandRunner::AcquireToken() {
538 + return true;
539 +}
540 +
541 bool DryRunCommandRunner::StartCommand(Edge* edge) {
542 finished_.push(edge);
543 return true;
544 }
545
546 -bool DryRunCommandRunner::WaitForCommand(Result* result) {
547 +bool DryRunCommandRunner::WaitForCommand(Result* result, bool more_ready) {
548 if (finished_.empty())
549 return false;
550
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();
561
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));
571 +}
572 +
573 +bool RealCommandRunner::AcquireToken() {
574 + return (!tokens_ || tokens_->Acquire());
575 }
576
577 bool RealCommandRunner::StartCommand(Edge* edge) {
578 @@ -507,14 +516,23 @@ bool RealCommandRunner::StartCommand(Edge* edge) {
579 return true;
580 }
581
582 -bool RealCommandRunner::WaitForCommand(Result* result) {
583 +bool RealCommandRunner::WaitForCommand(Result* result, bool more_ready) {
584 Subprocess* subproc;
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);
591 if (interrupted)
592 return false;
593 }
594
595 + // token became available
596 + if (subproc == NULL) {
597 + result->status = ExitTokenAvailable;
598 + return true;
599 + }
600 +
601 + // command completed
602 if (tokens_)
603 tokens_->Release();
604
605 @@ -639,9 +657,14 @@ bool Builder::Build(string* err) {
606 // command runner.
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();
617 +
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) {
630 Cleanup();
631 status_->BuildFinished();
632 @@ -678,6 +701,10 @@ bool Builder::Build(string* err) {
633 return false;
634 }
635
636 + // We might be able to start another command; start the main loop over.
637 + if (result.status == ExitTokenAvailable)
638 + continue;
639 +
640 --pending_commands;
641 if (!FinishCommand(&result, err)) {
642 Cleanup();
643 diff --git a/src/build.h b/src/build.h
644 index 7dcd111e61..35c7b97d12 100644
645 --- a/src/build.h
646 +++ b/src/build.h
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;
653
654 /// The result of waiting for a command.
655 @@ -150,7 +151,7 @@ struct CommandRunner {
656 bool success() const { return status == ExitSuccess; }
657 };
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;
661
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 {
669
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();
678
679 @@ -578,6 +579,10 @@ bool FakeCommandRunner::CanRunMore() const {
680 return active_edges_.size() < max_active_edges_;
681 }
682
683 +bool FakeCommandRunner::AcquireToken() {
684 + return true;
685 +}
686 +
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) {
691 return true;
692 }
693
694 -bool FakeCommandRunner::WaitForCommand(Result* result) {
695 +bool FakeCommandRunner::WaitForCommand(Result* result, bool more_ready) {
696 if (active_edges_.empty())
697 return false;
698
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
703 @@ -18,7 +18,8 @@
704 enum ExitStatus {
705 ExitSuccess,
706 ExitFailure,
707 - ExitInterrupted
708 + ExitTokenAvailable,
709 + ExitInterrupted,
710 };
711
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
717 @@ -13,6 +13,7 @@
718 // limitations under the License.
719
720 #include "subprocess.h"
721 +#include "tokenpool.h"
722
723 #include <sys/select.h>
724 #include <assert.h>
725 @@ -249,7 +250,7 @@ Subprocess *SubprocessSet::Add(const string& command, bool use_console) {
726 }
727
728 #ifdef USE_PPOLL
729 -bool SubprocessSet::DoWork() {
730 +bool SubprocessSet::DoWork(struct TokenPool* tokens) {
731 vector<pollfd> fds;
732 nfds_t nfds = 0;
733
734 @@ -263,6 +264,12 @@ bool SubprocessSet::DoWork() {
735 ++nfds;
736 }
737
738 + if (tokens) {
739 + pollfd pfd = { tokens->GetMonitorFd(), POLLIN | POLLPRI, 0 };
740 + fds.push_back(pfd);
741 + ++nfds;
742 + }
743 +
744 interrupted_ = 0;
745 int ret = ppoll(&fds.front(), nfds, NULL, &old_mask_);
746 if (ret == -1) {
747 @@ -295,11 +302,20 @@ bool SubprocessSet::DoWork() {
748 ++i;
749 }
750
751 + if (tokens) {
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;
757 + }
758 + }
759 +
760 return IsInterrupted();
761 }
762
763 #else // !defined(USE_PPOLL)
764 -bool SubprocessSet::DoWork() {
765 +bool SubprocessSet::DoWork(struct TokenPool* tokens) {
766 fd_set set;
767 int nfds = 0;
768 FD_ZERO(&set);
769 @@ -314,6 +330,13 @@ bool SubprocessSet::DoWork() {
770 }
771 }
772
773 + if (tokens) {
774 + int fd = tokens->GetMonitorFd();
775 + FD_SET(fd, &set);
776 + if (nfds < fd+1)
777 + nfds = fd+1;
778 + }
779 +
780 interrupted_ = 0;
781 int ret = pselect(nfds, &set, 0, 0, 0, &old_mask_);
782 if (ret == -1) {
783 @@ -342,6 +365,12 @@ bool SubprocessSet::DoWork() {
784 ++i;
785 }
786
787 + if (tokens) {
788 + int fd = tokens->GetMonitorFd();
789 + if ((fd >= 0) && FD_ISSET(fd, &set))
790 + token_available_ = true;
791 + }
792 +
793 return IsInterrupted();
794 }
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) {
801 return subprocess;
802 }
803
804 -bool SubprocessSet::DoWork() {
805 +bool SubprocessSet::DoWork(struct TokenPool* tokens) {
806 DWORD bytes_read;
807 Subprocess* subproc;
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;
815 };
816
817 +struct TokenPool;
818 +
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 {
823 ~SubprocessSet();
824
825 Subprocess* Add(const std::string& command, bool use_console = false);
826 - bool DoWork();
827 + bool DoWork(struct TokenPool* tokens);
828 Subprocess* NextFinished();
829 void Clear();
830
831 std::vector<Subprocess*> running_;
832 std::queue<Subprocess*> finished_;
833
834 + bool token_available_;
835 + bool IsTokenAvailable() { return token_available_; }
836 + void ResetTokenAvailable() { token_available_ = false; }
837 +
838 #ifdef _WIN32
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);
848
849 + subprocs_.ResetTokenAvailable();
850 while (!subproc->Done()) {
851 // Pretend we discovered that stderr was ready for writing.
852 - subprocs_.DoWork();
853 + subprocs_.DoWork(NULL);
854 }
855 + ASSERT_EQ(false, subprocs_.IsTokenAvailable());
856
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);
862
863 + subprocs_.ResetTokenAvailable();
864 while (!subproc->Done()) {
865 // Pretend we discovered that stderr was ready for writing.
866 - subprocs_.DoWork();
867 + subprocs_.DoWork(NULL);
868 }
869 + ASSERT_EQ(false, subprocs_.IsTokenAvailable());
870
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);
876
877 + subprocs_.ResetTokenAvailable();
878 while (!subproc->Done()) {
879 - subprocs_.DoWork();
880 + subprocs_.DoWork(NULL);
881 }
882 + ASSERT_EQ(false, subprocs_.IsTokenAvailable());
883
884 EXPECT_EQ(ExitInterrupted, subproc->Finish());
885 }
886 @@ -90,7 +96,7 @@ TEST_F(SubprocessTest, InterruptParent) {
887 ASSERT_NE((Subprocess *) 0, subproc);
888
889 while (!subproc->Done()) {
890 - bool interrupted = subprocs_.DoWork();
891 + bool interrupted = subprocs_.DoWork(NULL);
892 if (interrupted)
893 return;
894 }
895 @@ -102,9 +108,11 @@ TEST_F(SubprocessTest, InterruptChildWithSigTerm) {
896 Subprocess* subproc = subprocs_.Add("kill -TERM $$");
897 ASSERT_NE((Subprocess *) 0, subproc);
898
899 + subprocs_.ResetTokenAvailable();
900 while (!subproc->Done()) {
901 - subprocs_.DoWork();
902 + subprocs_.DoWork(NULL);
903 }
904 + ASSERT_EQ(false, subprocs_.IsTokenAvailable());
905
906 EXPECT_EQ(ExitInterrupted, subproc->Finish());
907 }
908 @@ -114,7 +122,7 @@ TEST_F(SubprocessTest, InterruptParentWithSigTerm) {
909 ASSERT_NE((Subprocess *) 0, subproc);
910
911 while (!subproc->Done()) {
912 - bool interrupted = subprocs_.DoWork();
913 + bool interrupted = subprocs_.DoWork(NULL);
914 if (interrupted)
915 return;
916 }
917 @@ -126,9 +134,11 @@ TEST_F(SubprocessTest, InterruptChildWithSigHup) {
918 Subprocess* subproc = subprocs_.Add("kill -HUP $$");
919 ASSERT_NE((Subprocess *) 0, subproc);
920
921 + subprocs_.ResetTokenAvailable();
922 while (!subproc->Done()) {
923 - subprocs_.DoWork();
924 + subprocs_.DoWork(NULL);
925 }
926 + ASSERT_EQ(false, subprocs_.IsTokenAvailable());
927
928 EXPECT_EQ(ExitInterrupted, subproc->Finish());
929 }
930 @@ -138,7 +148,7 @@ TEST_F(SubprocessTest, InterruptParentWithSigHup) {
931 ASSERT_NE((Subprocess *) 0, subproc);
932
933 while (!subproc->Done()) {
934 - bool interrupted = subprocs_.DoWork();
935 + bool interrupted = subprocs_.DoWork(NULL);
936 if (interrupted)
937 return;
938 }
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);
942
943 + subprocs_.ResetTokenAvailable();
944 while (!subproc->Done()) {
945 - subprocs_.DoWork();
946 + subprocs_.DoWork(NULL);
947 }
948 + ASSERT_EQ(false, subprocs_.IsTokenAvailable());
949
950 EXPECT_EQ(ExitSuccess, subproc->Finish());
951 }
952 @@ -167,9 +179,11 @@ TEST_F(SubprocessTest, SetWithSingle) {
953 Subprocess* subproc = subprocs_.Add(kSimpleCommand);
954 ASSERT_NE((Subprocess *) 0, subproc);
955
956 + subprocs_.ResetTokenAvailable();
957 while (!subproc->Done()) {
958 - subprocs_.DoWork();
959 + subprocs_.DoWork(NULL);
960 }
961 + ASSERT_EQ(false, subprocs_.IsTokenAvailable());
962 ASSERT_EQ(ExitSuccess, subproc->Finish());
963 ASSERT_NE("", subproc->GetOutput());
964
965 @@ -200,12 +214,13 @@ TEST_F(SubprocessTest, SetWithMulti) {
966 ASSERT_EQ("", processes[i]->GetOutput());
967 }
968
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);
975 }
976 -
977 + ASSERT_EQ(false, subprocs_.IsTokenAvailable());
978 ASSERT_EQ(0u, subprocs_.running_.size());
979 ASSERT_EQ(3u, subprocs_.finished_.size());
980
981 @@ -237,8 +252,10 @@ TEST_F(SubprocessTest, SetWithLots) {
982 ASSERT_NE((Subprocess *) 0, subproc);
983 procs.push_back(subproc);
984 }
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);
1001 }
1002 + ASSERT_EQ(false, subprocs_.IsTokenAvailable());
1003 ASSERT_EQ(ExitSuccess, subproc->Finish());
1004 ASSERT_EQ(1u, subprocs_.finished_.size());
1005 }
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();
1015
1016 bool Setup();
1017
1018 @@ -201,6 +202,10 @@ void GNUmakeTokenPool::Clear() {
1019 Return();
1020 }
1021
1022 +int GNUmakeTokenPool::GetMonitorFd() {
1023 + return(rfd_);
1024 +}
1025 +
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;
1036
1037 +#ifdef _WIN32
1038 + // @TODO
1039 +#else
1040 + virtual int GetMonitorFd() = 0;
1041 +#endif
1042 +
1043 // returns NULL if token pool is not available
1044 static struct TokenPool *Get(void);
1045 };
1046
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
1051
1052 This emulates the behaviour of GNU make.
1053
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
1063 ---
1064 src/build.cc | 10 ++++++----
1065 src/build.h | 4 +++-
1066 src/ninja.cc | 1 +
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(-)
1071
1072 diff --git a/src/build.cc b/src/build.cc
1073 index a25c349050..406a84ec39 100644
1074 --- a/src/build.cc
1075 +++ b/src/build.cc
1076 @@ -470,7 +470,7 @@ struct RealCommandRunner : public CommandRunner {
1077 };
1078
1079 RealCommandRunner::RealCommandRunner(const BuildConfig& config) : config_(config) {
1080 - tokens_ = TokenPool::Get();
1081 + tokens_ = TokenPool::Get(config_.parallelism_from_cmdline);
1082 }
1083
1084 RealCommandRunner::~RealCommandRunner() {
1085 @@ -492,9 +492,11 @@ void RealCommandRunner::Abort() {
1086 }
1087
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
1102 --- a/src/build.h
1103 +++ b/src/build.h
1104 @@ -159,7 +159,8 @@ struct CommandRunner {
1105
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) {}
1112
1113 enum Verbosity {
1114 @@ -171,6 +172,7 @@ struct BuildConfig {
1115 Verbosity verbosity;
1116 bool dry_run;
1117 int parallelism;
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
1124 --- a/src/ninja.cc
1125 +++ b/src/ninja.cc
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;
1132 break;
1133 }
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
1138 @@ -1,4 +1,4 @@
1139 -// Copyright 2016 Google Inc. All Rights Reserved.
1140 +// Copyright 2016-2017 Google Inc. All Rights Reserved.
1141 //
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();
1147
1148 - bool Setup();
1149 + bool Setup(bool ignore);
1150
1151 private:
1152 int available_;
1153 @@ -100,7 +100,7 @@ bool GNUmakeTokenPool::SetAlarmHandler() {
1154 }
1155 }
1156
1157 -bool GNUmakeTokenPool::Setup() {
1158 +bool GNUmakeTokenPool::Setup(bool ignore) {
1159 const char *value = getenv("MAKEFLAGS");
1160 if (value) {
1161 // GNU make <= 4.1
1162 @@ -109,16 +109,20 @@ bool GNUmakeTokenPool::Setup() {
1163 if (!jobserver)
1164 jobserver = strstr(value, "--jobserver-auth=");
1165 if (jobserver) {
1166 - int rfd = -1;
1167 - int wfd = -1;
1168 - if ((sscanf(jobserver, "%*[^=]=%d,%d", &rfd, &wfd) == 2) &&
1169 - CheckFd(rfd) &&
1170 - CheckFd(wfd) &&
1171 - SetAlarmHandler()) {
1172 - printf("ninja: using GNU make jobserver.\n");
1173 - rfd_ = rfd;
1174 - wfd_ = wfd;
1175 - return true;
1176 + if (ignore) {
1177 + printf("ninja: warning: -jN forced on command line; ignoring GNU make jobserver.\n");
1178 + } else {
1179 + int rfd = -1;
1180 + int wfd = -1;
1181 + if ((sscanf(jobserver, "%*[^=]=%d,%d", &rfd, &wfd) == 2) &&
1182 + CheckFd(rfd) &&
1183 + CheckFd(wfd) &&
1184 + SetAlarmHandler()) {
1185 + printf("ninja: using GNU make jobserver.\n");
1186 + rfd_ = rfd;
1187 + wfd_ = wfd;
1188 + return true;
1189 + }
1190 }
1191 }
1192 }
1193 @@ -206,9 +210,9 @@ int GNUmakeTokenPool::GetMonitorFd() {
1194 return(rfd_);
1195 }
1196
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))
1202 return tokenpool;
1203 else
1204 delete tokenpool;
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
1209 @@ -1,4 +1,4 @@
1210 -// Copyright 2016 Google Inc. All Rights Reserved.
1211 +// Copyright 2016-2017 Google Inc. All Rights Reserved.
1212 //
1213 // Licensed under the Apache License, Version 2.0 (the "License");
1214 // you may not use this file except in compliance with the License.
1215 @@ -22,6 +22,6 @@
1216 #include <stdlib.h>
1217
1218 // No-op TokenPool implementation
1219 -struct TokenPool *TokenPool::Get(void) {
1220 +struct TokenPool *TokenPool::Get(bool ignore) {
1221 return NULL;
1222 }
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
1227 @@ -1,4 +1,4 @@
1228 -// Copyright 2016 Google Inc. All Rights Reserved.
1229 +// Copyright 2016-2017 Google Inc. All Rights Reserved.
1230 //
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 {
1234 #endif
1235
1236 // returns NULL if token pool is not available
1237 - static struct TokenPool *Get(void);
1238 + static struct TokenPool *Get(bool ignore);
1239 };
1240
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
1245
1246 This emulates the behaviour of GNU make.
1247
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.
1251 ---
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(-)
1257
1258 diff --git a/src/build.cc b/src/build.cc
1259 index 406a84ec39..9e6272d035 100644
1260 --- a/src/build.cc
1261 +++ b/src/build.cc
1262 @@ -464,13 +464,17 @@ struct RealCommandRunner : public CommandRunner {
1263 virtual void Abort();
1264
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_;
1269 TokenPool *tokens_;
1270 map<const Subprocess*, Edge*> subproc_to_edge_;
1271 };
1272
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_);
1278 }
1279
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_));
1289 }
1290
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();
1299
1300 - bool Setup(bool ignore);
1301 + bool Setup(bool ignore, double& max_load_average);
1302
1303 private:
1304 int available_;
1305 @@ -100,7 +100,7 @@ bool GNUmakeTokenPool::SetAlarmHandler() {
1306 }
1307 }
1308
1309 -bool GNUmakeTokenPool::Setup(bool ignore) {
1310 +bool GNUmakeTokenPool::Setup(bool ignore, double& max_load_average) {
1311 const char *value = getenv("MAKEFLAGS");
1312 if (value) {
1313 // GNU make <= 4.1
1314 @@ -118,9 +118,20 @@ bool GNUmakeTokenPool::Setup(bool ignore) {
1315 CheckFd(rfd) &&
1316 CheckFd(wfd) &&
1317 SetAlarmHandler()) {
1318 + const char *l_arg = strstr(value, " -l");
1319 + int load_limit = -1;
1320 +
1321 printf("ninja: using GNU make jobserver.\n");
1322 rfd_ = rfd;
1323 wfd_ = wfd;
1324 +
1325 + // translate GNU make -lN to ninja -lN
1326 + if (l_arg &&
1327 + (sscanf(l_arg + 3, "%d ", &load_limit) == 1) &&
1328 + (load_limit > 0)) {
1329 + max_load_average = load_limit;
1330 + }
1331 +
1332 return true;
1333 }
1334 }
1335 @@ -210,9 +221,9 @@ int GNUmakeTokenPool::GetMonitorFd() {
1336 return(rfd_);
1337 }
1338
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))
1344 return tokenpool;
1345 else
1346 delete tokenpool;
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
1351 @@ -22,6 +22,6 @@
1352 #include <stdlib.h>
1353
1354 // No-op TokenPool implementation
1355 -struct TokenPool *TokenPool::Get(bool ignore) {
1356 +struct TokenPool *TokenPool::Get(bool ignore, double& max_load_average) {
1357 return NULL;
1358 }
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 {
1364 #endif
1365
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);
1369 };
1370
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
1375
1376 - replace printf() with calls to LinePrinter
1377 - print GNU make jobserver message only when verbose build is requested
1378 ---
1379 src/build.cc | 1 +
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(-)
1384
1385 diff --git a/src/build.cc b/src/build.cc
1386 index 9e6272d035..662e4bd7be 100644
1387 --- a/src/build.cc
1388 +++ b/src/build.cc
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,
1394 max_load_average_);
1395 }
1396
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
1401 @@ -23,6 +23,8 @@
1402 #include <string.h>
1403 #include <stdlib.h>
1404
1405 +#include "line_printer.h"
1406 +
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();
1413
1414 - bool Setup(bool ignore, double& max_load_average);
1415 + bool Setup(bool ignore, bool verbose, double& max_load_average);
1416
1417 private:
1418 int available_;
1419 @@ -100,7 +102,9 @@ bool GNUmakeTokenPool::SetAlarmHandler() {
1420 }
1421 }
1422
1423 -bool GNUmakeTokenPool::Setup(bool ignore, double& max_load_average) {
1424 +bool GNUmakeTokenPool::Setup(bool ignore,
1425 + bool verbose,
1426 + double& max_load_average) {
1427 const char *value = getenv("MAKEFLAGS");
1428 if (value) {
1429 // GNU make <= 4.1
1430 @@ -109,8 +113,10 @@ bool GNUmakeTokenPool::Setup(bool ignore, double& max_load_average) {
1431 if (!jobserver)
1432 jobserver = strstr(value, "--jobserver-auth=");
1433 if (jobserver) {
1434 + LinePrinter printer;
1435 +
1436 if (ignore) {
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");
1439 } else {
1440 int rfd = -1;
1441 int wfd = -1;
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;
1445
1446 - printf("ninja: using GNU make jobserver.\n");
1447 + if (verbose) {
1448 + printer.PrintOnNewLine("ninja: using GNU make jobserver.\n");
1449 + }
1450 rfd_ = rfd;
1451 wfd_ = wfd;
1452
1453 @@ -221,9 +229,11 @@ int GNUmakeTokenPool::GetMonitorFd() {
1454 return(rfd_);
1455 }
1456
1457 -struct TokenPool *TokenPool::Get(bool ignore, double& max_load_average) {
1458 +struct TokenPool *TokenPool::Get(bool ignore,
1459 + bool verbose,
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))
1464 return tokenpool;
1465 else
1466 delete tokenpool;
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
1471 @@ -22,6 +22,8 @@
1472 #include <stdlib.h>
1473
1474 // No-op TokenPool implementation
1475 -struct TokenPool *TokenPool::Get(bool ignore, double& max_load_average) {
1476 +struct TokenPool *TokenPool::Get(bool ignore,
1477 + bool verbose,
1478 + double& max_load_average) {
1479 return NULL;
1480 }
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 {
1486 #endif
1487
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,
1491 + bool verbose,
1492 + double& max_load_average);
1493 };
1494
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
1499
1500 - fix Windows build error in no-op TokenPool implementation
1501 - improve Acquire() to block for a maximum of 100ms
1502 - address review comments
1503 ---
1504 src/build.h | 2 ++
1505 src/tokenpool-gnu-make.cc | 53 +++++++++++++++++++++++++++++++++------
1506 src/tokenpool-none.cc | 7 +-----
1507 3 files changed, 49 insertions(+), 13 deletions(-)
1508
1509 diff --git a/src/build.h b/src/build.h
1510 index dfde576573..66ddefb888 100644
1511 --- a/src/build.h
1512 +++ b/src/build.h
1513 @@ -151,6 +151,8 @@ struct CommandRunner {
1514 bool success() const { return status == ExitSuccess; }
1515 };
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;
1520
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
1526 @@ -1,4 +1,4 @@
1527 -// Copyright 2016-2017 Google Inc. All Rights Reserved.
1528 +// Copyright 2016-2018 Google Inc. All Rights Reserved.
1529 //
1530 // Licensed under the Apache License, Version 2.0 (the "License");
1531 // you may not use this file except in compliance with the License.
1532 @@ -19,6 +19,7 @@
1533 #include <poll.h>
1534 #include <unistd.h>
1535 #include <signal.h>
1536 +#include <sys/time.h>
1537 #include <stdio.h>
1538 #include <string.h>
1539 #include <stdlib.h>
1540 @@ -153,6 +154,15 @@ bool GNUmakeTokenPool::Acquire() {
1541 if (available_ > 0)
1542 return true;
1543
1544 + // Please read
1545 + //
1546 + // http://make.mad-scientist.net/papers/jobserver-implementation/
1547 + //
1548 + // for the reasoning behind the following code.
1549 + //
1550 + // Try to read one character from the pipe. Returns true on success.
1551 + //
1552 + // First check if read() would succeed without blocking.
1553 #ifdef USE_PPOLL
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);
1558 #endif
1559 if (ret > 0) {
1560 + // Handle potential race condition:
1561 + // - the above check succeeded, i.e. read() should not block
1562 + // - the character disappears before we call read()
1563 + //
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_);
1567
1568 if (dup_rfd_ != -1) {
1569 struct sigaction act, old_act;
1570 int ret = 0;
1571
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) {
1576 - char buf;
1577 -
1578 - // block until token read, child exits or timeout
1579 - alarm(1);
1580 - ret = read(dup_rfd_, &buf, 1);
1581 - alarm(0);
1582 + struct itimerval timeout;
1583 +
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) {
1588 + char buf;
1589 +
1590 + // Now try to read() from dup_rfd_. Return values from read():
1591 + //
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);
1600 +
1601 + // disarm timer
1602 + memset(&timeout, 0, sizeof(timeout));
1603 + setitimer(ITIMER_REAL, &timeout, NULL);
1604 + }
1605
1606 sigaction(SIGCHLD, &old_act, NULL);
1607 }
1608
1609 CloseDupRfd(0);
1610
1611 + // Case 1 from above list
1612 if (ret > 0) {
1613 available_++;
1614 return true;
1615 }
1616 }
1617 }
1618 +
1619 + // read() would block, i.e. no token available,
1620 + // cases 2-6 from above list or
1621 + // select() / poll() / dup() / sigaction() / setitimer() failed
1622 return false;
1623 }
1624
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
1629 @@ -1,4 +1,4 @@
1630 -// Copyright 2016-2017 Google Inc. All Rights Reserved.
1631 +// Copyright 2016-2018 Google Inc. All Rights Reserved.
1632 //
1633 // Licensed under the Apache License, Version 2.0 (the "License");
1634 // you may not use this file except in compliance with the License.
1635 @@ -14,11 +14,6 @@
1636
1637 #include "tokenpool.h"
1638
1639 -#include <fcntl.h>
1640 -#include <poll.h>
1641 -#include <unistd.h>
1642 -#include <stdio.h>
1643 -#include <string.h>
1644 #include <stdlib.h>
1645
1646 // No-op TokenPool implementation
1647
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
1652
1653 - TokenPool setup
1654 - GetMonitorFd() API
1655 - implicit token and tokens in jobserver pipe
1656 - Acquire() / Reserve() / Release() protocol
1657 - Clear() API
1658 ---
1659 configure.py | 1 +
1660 src/tokenpool_test.cc | 198 ++++++++++++++++++++++++++++++++++++++++++
1661 2 files changed, 199 insertions(+)
1662 create mode 100644 src/tokenpool_test.cc
1663
1664 diff --git a/configure.py b/configure.py
1665 index db3492c93c..dc8a0066b7 100755
1666 --- a/configure.py
1667 +++ b/configure.py
1668 @@ -590,6 +590,7 @@ def has_re2c():
1669 'string_piece_util_test',
1670 'subprocess_test',
1671 'test',
1672 + 'tokenpool_test',
1673 '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
1679 --- /dev/null
1680 +++ b/src/tokenpool_test.cc
1681 @@ -0,0 +1,198 @@
1682 +// Copyright 2018 Google Inc. All Rights Reserved.
1683 +//
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
1687 +//
1688 +// http://www.apache.org/licenses/LICENSE-2.0
1689 +//
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.
1695 +
1696 +#include "tokenpool.h"
1697 +
1698 +#include "test.h"
1699 +
1700 +#ifndef _WIN32
1701 +#include <stdio.h>
1702 +#include <stdlib.h>
1703 +#include <unistd.h>
1704 +
1705 +#define ENVIRONMENT_CLEAR() unsetenv("MAKEFLAGS")
1706 +#define ENVIRONMENT_INIT(v) setenv("MAKEFLAGS", v, true);
1707 +#endif
1708 +
1709 +namespace {
1710 +
1711 +const double kLoadAverageDefault = -1.23456789;
1712 +
1713 +struct TokenPoolTest : public testing::Test {
1714 + double load_avg_;
1715 + TokenPool *tokens_;
1716 +#ifndef _WIN32
1717 + char buf_[1024];
1718 + int fds_[2];
1719 +#endif
1720 +
1721 + virtual void SetUp() {
1722 + load_avg_ = kLoadAverageDefault;
1723 + tokens_ = NULL;
1724 +#ifndef _WIN32
1725 + ENVIRONMENT_CLEAR();
1726 + if (pipe(fds_) < 0)
1727 + ASSERT_TRUE(false);
1728 +#endif
1729 + }
1730 +
1731 + void CreatePool(const char *format, bool ignore_jobserver) {
1732 +#ifndef _WIN32
1733 + if (format) {
1734 + sprintf(buf_, format, fds_[0], fds_[1]);
1735 + ENVIRONMENT_INIT(buf_);
1736 + }
1737 +#endif
1738 + tokens_ = TokenPool::Get(ignore_jobserver, false, load_avg_);
1739 + }
1740 +
1741 + void CreateDefaultPool() {
1742 + CreatePool("foo --jobserver-auth=%d,%d bar", false);
1743 + }
1744 +
1745 + virtual void TearDown() {
1746 + if (tokens_)
1747 + delete tokens_;
1748 +#ifndef _WIN32
1749 + close(fds_[0]);
1750 + close(fds_[1]);
1751 + ENVIRONMENT_CLEAR();
1752 +#endif
1753 + }
1754 +};
1755 +
1756 +} // anonymous namespace
1757 +
1758 +// verifies none implementation
1759 +TEST_F(TokenPoolTest, NoTokenPool) {
1760 + CreatePool(NULL, false);
1761 +
1762 + EXPECT_EQ(NULL, tokens_);
1763 + EXPECT_EQ(kLoadAverageDefault, load_avg_);
1764 +}
1765 +
1766 +#ifndef _WIN32
1767 +TEST_F(TokenPoolTest, SuccessfulOldSetup) {
1768 + // GNUmake <= 4.1
1769 + CreatePool("foo --jobserver-fds=%d,%d bar", false);
1770 +
1771 + EXPECT_NE(NULL, tokens_);
1772 + EXPECT_EQ(kLoadAverageDefault, load_avg_);
1773 +}
1774 +
1775 +TEST_F(TokenPoolTest, SuccessfulNewSetup) {
1776 + // GNUmake => 4.2
1777 + CreateDefaultPool();
1778 +
1779 + EXPECT_NE(NULL, tokens_);
1780 + EXPECT_EQ(kLoadAverageDefault, load_avg_);
1781 +}
1782 +
1783 +TEST_F(TokenPoolTest, IgnoreWithJN) {
1784 + CreatePool("foo --jobserver-auth=%d,%d bar", true);
1785 +
1786 + EXPECT_EQ(NULL, tokens_);
1787 + EXPECT_EQ(kLoadAverageDefault, load_avg_);
1788 +}
1789 +
1790 +TEST_F(TokenPoolTest, HonorLN) {
1791 + CreatePool("foo -l9 --jobserver-auth=%d,%d bar", false);
1792 +
1793 + EXPECT_NE(NULL, tokens_);
1794 + EXPECT_EQ(9.0, load_avg_);
1795 +}
1796 +
1797 +TEST_F(TokenPoolTest, MonitorFD) {
1798 + CreateDefaultPool();
1799 +
1800 + ASSERT_NE(NULL, tokens_);
1801 + EXPECT_EQ(kLoadAverageDefault, load_avg_);
1802 +
1803 + EXPECT_EQ(fds_[0], tokens_->GetMonitorFd());
1804 +}
1805 +
1806 +TEST_F(TokenPoolTest, ImplicitToken) {
1807 + CreateDefaultPool();
1808 +
1809 + ASSERT_NE(NULL, tokens_);
1810 + EXPECT_EQ(kLoadAverageDefault, load_avg_);
1811 +
1812 + EXPECT_TRUE(tokens_->Acquire());
1813 + tokens_->Reserve();
1814 + EXPECT_FALSE(tokens_->Acquire());
1815 + tokens_->Release();
1816 + EXPECT_TRUE(tokens_->Acquire());
1817 +}
1818 +
1819 +TEST_F(TokenPoolTest, TwoTokens) {
1820 + CreateDefaultPool();
1821 +
1822 + ASSERT_NE(NULL, tokens_);
1823 + EXPECT_EQ(kLoadAverageDefault, load_avg_);
1824 +
1825 + // implicit token
1826 + EXPECT_TRUE(tokens_->Acquire());
1827 + tokens_->Reserve();
1828 + EXPECT_FALSE(tokens_->Acquire());
1829 +
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());
1835 +
1836 + // release 2nd token
1837 + tokens_->Release();
1838 + EXPECT_TRUE(tokens_->Acquire());
1839 +
1840 + // release implict token - must return 2nd token back to jobserver
1841 + tokens_->Release();
1842 + EXPECT_TRUE(tokens_->Acquire());
1843 +
1844 + // there must be one token in the pipe
1845 + EXPECT_EQ(1u, read(fds_[0], buf_, sizeof(buf_)));
1846 +
1847 + // implicit token
1848 + EXPECT_TRUE(tokens_->Acquire());
1849 +}
1850 +
1851 +TEST_F(TokenPoolTest, Clear) {
1852 + CreateDefaultPool();
1853 +
1854 + ASSERT_NE(NULL, tokens_);
1855 + EXPECT_EQ(kLoadAverageDefault, load_avg_);
1856 +
1857 + // implicit token
1858 + EXPECT_TRUE(tokens_->Acquire());
1859 + tokens_->Reserve();
1860 + EXPECT_FALSE(tokens_->Acquire());
1861 +
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());
1869 +
1870 + tokens_->Clear();
1871 + EXPECT_TRUE(tokens_->Acquire());
1872 +
1873 + // there must be two tokens in the pipe
1874 + EXPECT_EQ(2u, read(fds_[0], buf_, sizeof(buf_)));
1875 +
1876 + // implicit token
1877 + EXPECT_TRUE(tokens_->Acquire());
1878 +}
1879 +#endif
1880
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
1885
1886 - add TokenPoolTest stub to provide TokenPool::GetMonitorFd()
1887 - add two tests
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
1893 ---
1894 src/subprocess_test.cc | 76 ++++++++++++++++++++++++++++++++++++++++++
1895 1 file changed, 76 insertions(+)
1896
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
1901 @@ -13,6 +13,7 @@
1902 // limitations under the License.
1903
1904 #include "subprocess.h"
1905 +#include "tokenpool.h"
1906
1907 #include "test.h"
1908
1909 @@ -34,8 +35,23 @@ const char* kSimpleCommand = "cmd /c dir \\";
1910 const char* kSimpleCommand = "ls /";
1911 #endif
1912
1913 +struct TokenPoolTest : public TokenPool {
1914 + bool Acquire() { return false; }
1915 + void Reserve() {}
1916 + void Release() {}
1917 + void Clear() {}
1918 +
1919 +#ifdef _WIN32
1920 + // @TODO
1921 +#else
1922 + int _fd;
1923 + int GetMonitorFd() { return _fd; }
1924 +#endif
1925 +};
1926 +
1927 struct SubprocessTest : public testing::Test {
1928 SubprocessSet subprocs_;
1929 + TokenPoolTest tokens_;
1930 };
1931
1932 } // anonymous namespace
1933 @@ -280,3 +296,63 @@ TEST_F(SubprocessTest, ReadStdin) {
1934 ASSERT_EQ(1u, subprocs_.finished_.size());
1935 }
1936 #endif // _WIN32
1937 +
1938 +// @TODO: remove once TokenPool implementation for Windows is available
1939 +#ifndef _WIN32
1940 +TEST_F(SubprocessTest, TokenAvailable) {
1941 + Subprocess* subproc = subprocs_.Add(kSimpleCommand);
1942 + ASSERT_NE((Subprocess *) 0, subproc);
1943 +
1944 + // simulate GNUmake jobserver pipe with 1 token
1945 + int fds[2];
1946 + ASSERT_EQ(0u, pipe(fds));
1947 + tokens_._fd = fds[0];
1948 + ASSERT_EQ(1u, write(fds[1], "T", 1));
1949 +
1950 + subprocs_.ResetTokenAvailable();
1951 + subprocs_.DoWork(&tokens_);
1952 +
1953 + EXPECT_TRUE(subprocs_.IsTokenAvailable());
1954 + EXPECT_EQ(0u, subprocs_.finished_.size());
1955 +
1956 + // remove token to let DoWork() wait for command again
1957 + char token;
1958 + ASSERT_EQ(1u, read(fds[0], &token, 1));
1959 +
1960 + while (!subproc->Done()) {
1961 + subprocs_.DoWork(&tokens_);
1962 + }
1963 +
1964 + close(fds[1]);
1965 + close(fds[0]);
1966 +
1967 + EXPECT_EQ(ExitSuccess, subproc->Finish());
1968 + EXPECT_NE("", subproc->GetOutput());
1969 +
1970 + EXPECT_EQ(1u, subprocs_.finished_.size());
1971 +}
1972 +
1973 +TEST_F(SubprocessTest, TokenNotAvailable) {
1974 + Subprocess* subproc = subprocs_.Add(kSimpleCommand);
1975 + ASSERT_NE((Subprocess *) 0, subproc);
1976 +
1977 + // simulate GNUmake jobserver pipe with 0 tokens
1978 + int fds[2];
1979 + ASSERT_EQ(0u, pipe(fds));
1980 + tokens_._fd = fds[0];
1981 +
1982 + subprocs_.ResetTokenAvailable();
1983 + while (!subproc->Done()) {
1984 + subprocs_.DoWork(&tokens_);
1985 + }
1986 +
1987 + close(fds[1]);
1988 + close(fds[0]);
1989 +
1990 + EXPECT_FALSE(subprocs_.IsTokenAvailable());
1991 + EXPECT_EQ(ExitSuccess, subproc->Finish());
1992 + EXPECT_NE("", subproc->GetOutput());
1993 +
1994 + EXPECT_EQ(1u, subprocs_.finished_.size());
1995 +}
1996 +#endif // _WIN32
1997
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
2002
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
2006 WaitForCommand().
2007 ---
2008 src/build_test.cc | 364 ++++++++++++++++++++++++++++++++++++++++++++++
2009 1 file changed, 364 insertions(+)
2010
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
2015 @@ -15,6 +15,7 @@
2016 #include "build.h"
2017
2018 #include <assert.h>
2019 +#include <stdarg.h>
2020
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);
2026 }
2027 +
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!
2031 +
2032 +/// Fake implementation of CommandRunner that simulates a TokenPool
2033 +struct FakeTokenCommandRunner : public CommandRunner {
2034 + explicit FakeTokenCommandRunner() {}
2035 +
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();
2043 +
2044 + vector<string> commands_ran_;
2045 + vector<Edge *> edges_;
2046 +
2047 + vector<bool> acquire_token_;
2048 + vector<bool> can_run_more_;
2049 + vector<bool> wait_for_command_;
2050 +};
2051 +
2052 +bool FakeTokenCommandRunner::CanRunMore() const {
2053 + if (can_run_more_.size() == 0) {
2054 + EXPECT_FALSE("unexpected call to CommandRunner::CanRunMore()");
2055 + return false;
2056 + }
2057 +
2058 + bool result = can_run_more_[0];
2059 +
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()
2063 + );
2064 +
2065 + return result;
2066 +}
2067 +
2068 +bool FakeTokenCommandRunner::AcquireToken() {
2069 + if (acquire_token_.size() == 0) {
2070 + EXPECT_FALSE("unexpected call to CommandRunner::AcquireToken()");
2071 + return false;
2072 + }
2073 +
2074 + bool result = acquire_token_[0];
2075 + acquire_token_.erase(acquire_token_.begin());
2076 + return result;
2077 +}
2078 +
2079 +bool FakeTokenCommandRunner::StartCommand(Edge* edge) {
2080 + commands_ran_.push_back(edge->EvaluateCommand());
2081 + edges_.push_back(edge);
2082 + return true;
2083 +}
2084 +
2085 +bool FakeTokenCommandRunner::WaitForCommand(Result* result, bool more_ready) {
2086 + if (wait_for_command_.size() == 0) {
2087 + EXPECT_FALSE("unexpected call to CommandRunner::WaitForCommand()");
2088 + return false;
2089 + }
2090 +
2091 + bool expected = wait_for_command_[0];
2092 + if (expected != more_ready) {
2093 + EXPECT_EQ(expected, more_ready);
2094 + return false;
2095 + }
2096 + wait_for_command_.erase(wait_for_command_.begin());
2097 +
2098 + if (edges_.size() == 0)
2099 + return false;
2100 +
2101 + Edge* edge = edges_[0];
2102 + result->edge = edge;
2103 +
2104 + if (more_ready &&
2105 + (edge->rule().name() == "token-available")) {
2106 + result->status = ExitTokenAvailable;
2107 + } else {
2108 + edges_.erase(edges_.begin());
2109 + result->status = ExitSuccess;
2110 + }
2111 +
2112 + return true;
2113 +}
2114 +
2115 +vector<Edge*> FakeTokenCommandRunner::GetActiveEdges() {
2116 + return edges_;
2117 +}
2118 +
2119 +void FakeTokenCommandRunner::Abort() {
2120 + edges_.clear();
2121 +}
2122 +
2123 +struct BuildTokenTest : public BuildTest {
2124 + virtual void SetUp();
2125 + virtual void TearDown();
2126 +
2127 + FakeTokenCommandRunner token_command_runner_;
2128 +
2129 + void ExpectAcquireToken(int count, ...);
2130 + void ExpectCanRunMore(int count, ...);
2131 + void ExpectWaitForCommand(int count, ...);
2132 +
2133 +private:
2134 + void EnqueueBooleans(vector<bool>& booleans, int count, va_list ao);
2135 +};
2136 +
2137 +void BuildTokenTest::SetUp() {
2138 + BuildTest::SetUp();
2139 +
2140 + // replace FakeCommandRunner with FakeTokenCommandRunner
2141 + builder_.command_runner_.release();
2142 + builder_.command_runner_.reset(&token_command_runner_);
2143 +}
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());
2148 +
2149 + BuildTest::TearDown();
2150 +}
2151 +
2152 +void BuildTokenTest::ExpectAcquireToken(int count, ...) {
2153 + va_list ap;
2154 + va_start(ap, count);
2155 + EnqueueBooleans(token_command_runner_.acquire_token_, count, ap);
2156 + va_end(ap);
2157 +}
2158 +
2159 +void BuildTokenTest::ExpectCanRunMore(int count, ...) {
2160 + va_list ap;
2161 + va_start(ap, count);
2162 + EnqueueBooleans(token_command_runner_.can_run_more_, count, ap);
2163 + va_end(ap);
2164 +}
2165 +
2166 +void BuildTokenTest::ExpectWaitForCommand(int count, ...) {
2167 + va_list ap;
2168 + va_start(ap, count);
2169 + EnqueueBooleans(token_command_runner_.wait_for_command_, count, ap);
2170 + va_end(ap);
2171 +}
2172 +
2173 +void BuildTokenTest::EnqueueBooleans(vector<bool>& booleans, int count, va_list ap) {
2174 + while (count--) {
2175 + int value = va_arg(ap, int);
2176 + booleans.push_back(!!value); // force bool
2177 + }
2178 +}
2179 +
2180 +TEST_F(BuildTokenTest, CompleteNoWork) {
2181 + // plan should not execute anything
2182 + string err;
2183 +
2184 + EXPECT_TRUE(builder_.Build(&err));
2185 + EXPECT_EQ("", err);
2186 +
2187 + EXPECT_EQ(0u, token_command_runner_.commands_ran_.size());
2188 +}
2189 +
2190 +TEST_F(BuildTokenTest, DoNotAquireToken) {
2191 + // plan should execute one command
2192 + string err;
2193 + EXPECT_TRUE(builder_.AddTarget("cat1", &err));
2194 + ASSERT_EQ("", err);
2195 +
2196 + // pretend we can't run anything
2197 + ExpectCanRunMore(1, false);
2198 +
2199 + EXPECT_FALSE(builder_.Build(&err));
2200 + EXPECT_EQ("stuck [this is a bug]", err);
2201 +
2202 + EXPECT_EQ(0u, token_command_runner_.commands_ran_.size());
2203 +}
2204 +
2205 +TEST_F(BuildTokenTest, DoNotStartWithoutToken) {
2206 + // plan should execute one command
2207 + string err;
2208 + EXPECT_TRUE(builder_.AddTarget("cat1", &err));
2209 + ASSERT_EQ("", err);
2210 +
2211 + // we could run a command but do not have a token for it
2212 + ExpectCanRunMore(1, true);
2213 + ExpectAcquireToken(1, false);
2214 +
2215 + EXPECT_FALSE(builder_.Build(&err));
2216 + EXPECT_EQ("stuck [this is a bug]", err);
2217 +
2218 + EXPECT_EQ(0u, token_command_runner_.commands_ran_.size());
2219 +}
2220 +
2221 +TEST_F(BuildTokenTest, CompleteOneStep) {
2222 + // plan should execute one command
2223 + string err;
2224 + EXPECT_TRUE(builder_.AddTarget("cat1", &err));
2225 + ASSERT_EQ("", err);
2226 +
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);
2232 +
2233 + EXPECT_TRUE(builder_.Build(&err));
2234 + EXPECT_EQ("", err);
2235 +
2236 + EXPECT_EQ(1u, token_command_runner_.commands_ran_.size());
2237 + EXPECT_TRUE(token_command_runner_.commands_ran_[0] == "cat in1 > cat1");
2238 +}
2239 +
2240 +TEST_F(BuildTokenTest, AcquireOneToken) {
2241 + // plan should execute more than one command
2242 + string err;
2243 + EXPECT_TRUE(builder_.AddTarget("cat12", &err));
2244 + ASSERT_EQ("", err);
2245 +
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);
2251 +
2252 + EXPECT_FALSE(builder_.Build(&err));
2253 + EXPECT_EQ("stuck [this is a bug]", err);
2254 +
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");
2259 +}
2260 +
2261 +TEST_F(BuildTokenTest, WantTwoTokens) {
2262 + // plan should execute more than one command
2263 + string err;
2264 + EXPECT_TRUE(builder_.AddTarget("cat12", &err));
2265 + ASSERT_EQ("", err);
2266 +
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);
2272 +
2273 + EXPECT_FALSE(builder_.Build(&err));
2274 + EXPECT_EQ("stuck [this is a bug]", err);
2275 +
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");
2280 +}
2281 +
2282 +TEST_F(BuildTokenTest, CompleteTwoSteps) {
2283 + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
2284 +"build out1: cat in1\n"
2285 +"build out2: cat out1\n"));
2286 +
2287 + // plan should execute more than one command
2288 + string err;
2289 + EXPECT_TRUE(builder_.AddTarget("out2", &err));
2290 + ASSERT_EQ("", err);
2291 +
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);
2297 +
2298 + EXPECT_TRUE(builder_.Build(&err));
2299 + EXPECT_EQ("", err);
2300 +
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");
2304 +}
2305 +
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"));
2313 +
2314 + // plan should execute more than one command
2315 + string err;
2316 + EXPECT_TRUE(builder_.AddTarget("out12", &err));
2317 + ASSERT_EQ("", err);
2318 +
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);
2327 +
2328 + EXPECT_FALSE(builder_.Build(&err));
2329 + EXPECT_EQ("stuck [this is a bug]", err);
2330 +
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"));
2336 +}
2337 +
2338 +TEST_F(BuildTokenTest, CompleteThreeStepsSerial) {
2339 + // plan should execute more than one command
2340 + string err;
2341 + EXPECT_TRUE(builder_.AddTarget("cat12", &err));
2342 + ASSERT_EQ("", err);
2343 +
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);
2349 +
2350 + EXPECT_TRUE(builder_.Build(&err));
2351 + EXPECT_EQ("", err);
2352 +
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");
2359 +}
2360 +
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"));
2368 +
2369 + // plan should execute more than one command
2370 + string err;
2371 + EXPECT_TRUE(builder_.AddTarget("out12", &err));
2372 + ASSERT_EQ("", err);
2373 +
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);
2379 +
2380 + EXPECT_TRUE(builder_.Build(&err));
2381 + EXPECT_EQ("", err);
2382 +
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");
2389 +}
2390
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
2395
2396 GNU make uses a semaphore as jobserver protocol on Win32. See also
2397
2398 https://www.gnu.org/software/make/manual/html_node/Windows-Jobserver.html
2399
2400 Usage is pretty simple and straightforward, i.e. WaitForSingleObject()
2401 to obtain a token and ReleaseSemaphore() to return it.
2402
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.
2406
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.
2411
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
2418 ---
2419 configure.py | 8 +-
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
2434
2435 diff --git a/configure.py b/configure.py
2436 index dc8a0066b7..a239b90eef 100755
2437 --- a/configure.py
2438 +++ b/configure.py
2439 @@ -517,12 +517,13 @@ def has_re2c():
2440 'state',
2441 'status',
2442 'string_piece_util',
2443 + 'tokenpool-gnu-make',
2444 'util',
2445 'version']:
2446 objs += cxx(name, variables=cxxvariables)
2447 if platform.is_windows():
2448 for name in ['subprocess-win32',
2449 - 'tokenpool-none',
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')
2457 else:
2458 - objs += cxx('subprocess-posix')
2459 - objs += cxx('tokenpool-gnu-make')
2460 + for name in ['subprocess-posix',
2461 + 'tokenpool-gnu-make-posix']:
2462 + objs += cxx(name)
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
2468 --- a/src/build.cc
2469 +++ b/src/build.cc
2470 @@ -473,9 +473,14 @@ struct RealCommandRunner : public CommandRunner {
2471
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_)) {
2481 + delete tokens_;
2482 + tokens_ = NULL;
2483 + }
2484 + }
2485 }
2486
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
2492 @@ -13,6 +13,7 @@
2493 // limitations under the License.
2494
2495 #include "subprocess.h"
2496 +#include "tokenpool.h"
2497
2498 #include <assert.h>
2499 #include <stdio.h>
2500 @@ -256,6 +257,9 @@ bool SubprocessSet::DoWork(struct TokenPool* tokens) {
2501 Subprocess* subproc;
2502 OVERLAPPED* overlapped;
2503
2504 + if (tokens)
2505 + tokens->WaitForTokenAvailability(ioport_);
2506 +
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.
2512 return true;
2513
2514 + if (tokens && tokens->TokenIsAvailable((ULONG_PTR)subproc)) {
2515 + token_available_ = true;
2516 + return false;
2517 + }
2518 +
2519 subproc->OnPipeReady();
2520
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 {
2527 void Reserve() {}
2528 void Release() {}
2529 void Clear() {}
2530 + bool Setup(bool ignore_unused, bool verbose, double& max_load_average) { return false; }
2531
2532 #ifdef _WIN32
2533 - // @TODO
2534 + bool _token_available;
2535 + void WaitForTokenAvailability(HANDLE ioport) {
2536 + if (_token_available)
2537 + // unblock GetQueuedCompletionStatus()
2538 + PostQueuedCompletionStatus(ioport, 0, (ULONG_PTR) this, NULL);
2539 + }
2540 + bool TokenIsAvailable(ULONG_PTR key) { return key == (ULONG_PTR) this; }
2541 #else
2542 int _fd;
2543 int GetMonitorFd() { return _fd; }
2544 @@ -297,34 +304,48 @@ TEST_F(SubprocessTest, ReadStdin) {
2545 }
2546 #endif // _WIN32
2547
2548 -// @TODO: remove once TokenPool implementation for Windows is available
2549 -#ifndef _WIN32
2550 TEST_F(SubprocessTest, TokenAvailable) {
2551 Subprocess* subproc = subprocs_.Add(kSimpleCommand);
2552 ASSERT_NE((Subprocess *) 0, subproc);
2553
2554 // simulate GNUmake jobserver pipe with 1 token
2555 +#ifdef _WIN32
2556 + tokens_._token_available = true;
2557 +#else
2558 int fds[2];
2559 ASSERT_EQ(0u, pipe(fds));
2560 tokens_._fd = fds[0];
2561 ASSERT_EQ(1u, write(fds[1], "T", 1));
2562 +#endif
2563
2564 subprocs_.ResetTokenAvailable();
2565 subprocs_.DoWork(&tokens_);
2566 +#ifdef _WIN32
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_);
2572 + }
2573 +#endif
2574
2575 EXPECT_TRUE(subprocs_.IsTokenAvailable());
2576 EXPECT_EQ(0u, subprocs_.finished_.size());
2577
2578 // remove token to let DoWork() wait for command again
2579 +#ifndef _WIN32
2580 char token;
2581 ASSERT_EQ(1u, read(fds[0], &token, 1));
2582 +#endif
2583
2584 while (!subproc->Done()) {
2585 subprocs_.DoWork(&tokens_);
2586 }
2587
2588 +#ifndef _WIN32
2589 close(fds[1]);
2590 close(fds[0]);
2591 +#endif
2592
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);
2597
2598 // simulate GNUmake jobserver pipe with 0 tokens
2599 +#ifdef _WIN32
2600 + tokens_._token_available = false;
2601 +#else
2602 int fds[2];
2603 ASSERT_EQ(0u, pipe(fds));
2604 tokens_._fd = fds[0];
2605 +#endif
2606
2607 subprocs_.ResetTokenAvailable();
2608 while (!subproc->Done()) {
2609 subprocs_.DoWork(&tokens_);
2610 }
2611
2612 +#ifndef _WIN32
2613 close(fds[1]);
2614 close(fds[0]);
2615 +#endif
2616
2617 EXPECT_FALSE(subprocs_.IsTokenAvailable());
2618 EXPECT_EQ(ExitSuccess, subproc->Finish());
2619 @@ -355,4 +382,3 @@ TEST_F(SubprocessTest, TokenNotAvailable) {
2620
2621 EXPECT_EQ(1u, subprocs_.finished_.size());
2622 }
2623 -#endif // _WIN32
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
2627 --- /dev/null
2628 +++ b/src/tokenpool-gnu-make-posix.cc
2629 @@ -0,0 +1,203 @@
2630 +// Copyright 2016-2018 Google Inc. All Rights Reserved.
2631 +//
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
2635 +//
2636 +// http://www.apache.org/licenses/LICENSE-2.0
2637 +//
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.
2643 +
2644 +#include "tokenpool-gnu-make.h"
2645 +
2646 +#include <errno.h>
2647 +#include <fcntl.h>
2648 +#include <poll.h>
2649 +#include <unistd.h>
2650 +#include <signal.h>
2651 +#include <sys/time.h>
2652 +#include <stdio.h>
2653 +#include <string.h>
2654 +#include <stdlib.h>
2655 +
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();
2661 +
2662 + virtual int GetMonitorFd();
2663 +
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();
2668 +
2669 + private:
2670 + int rfd_;
2671 + int wfd_;
2672 +
2673 + struct sigaction old_act_;
2674 + bool restore_;
2675 +
2676 + static int dup_rfd_;
2677 + static void CloseDupRfd(int signum);
2678 +
2679 + bool CheckFd(int fd);
2680 + bool SetAlarmHandler();
2681 +};
2682 +
2683 +GNUmakeTokenPoolPosix::GNUmakeTokenPoolPosix() : rfd_(-1), wfd_(-1), restore_(false) {
2684 +}
2685 +
2686 +GNUmakeTokenPoolPosix::~GNUmakeTokenPoolPosix() {
2687 + Clear();
2688 + if (restore_)
2689 + sigaction(SIGALRM, &old_act_, NULL);
2690 +}
2691 +
2692 +bool GNUmakeTokenPoolPosix::CheckFd(int fd) {
2693 + if (fd < 0)
2694 + return false;
2695 + int ret = fcntl(fd, F_GETFD);
2696 + if (ret < 0)
2697 + return false;
2698 + return true;
2699 +}
2700 +
2701 +int GNUmakeTokenPoolPosix::dup_rfd_ = -1;
2702 +
2703 +void GNUmakeTokenPoolPosix::CloseDupRfd(int signum) {
2704 + close(dup_rfd_);
2705 + dup_rfd_ = -1;
2706 +}
2707 +
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:");
2714 + return(false);
2715 + } else {
2716 + restore_ = true;
2717 + return(true);
2718 + }
2719 +}
2720 +
2721 +bool GNUmakeTokenPoolPosix::ParseAuth(const char *jobserver) {
2722 + int rfd = -1;
2723 + int wfd = -1;
2724 + if ((sscanf(jobserver, "%*[^=]=%d,%d", &rfd, &wfd) == 2) &&
2725 + CheckFd(rfd) &&
2726 + CheckFd(wfd) &&
2727 + SetAlarmHandler()) {
2728 + rfd_ = rfd;
2729 + wfd_ = wfd;
2730 + return true;
2731 + }
2732 +
2733 + return false;
2734 +}
2735 +
2736 +bool GNUmakeTokenPoolPosix::AcquireToken() {
2737 + // Please read
2738 + //
2739 + // http://make.mad-scientist.net/papers/jobserver-implementation/
2740 + //
2741 + // for the reasoning behind the following code.
2742 + //
2743 + // Try to read one character from the pipe. Returns true on success.
2744 + //
2745 + // First check if read() would succeed without blocking.
2746 +#ifdef USE_PPOLL
2747 + pollfd pollfds[] = {{rfd_, POLLIN, 0}};
2748 + int ret = poll(pollfds, 1, 0);
2749 +#else
2750 + fd_set set;
2751 + struct timeval timeout = { 0, 0 };
2752 + FD_ZERO(&set);
2753 + FD_SET(rfd_, &set);
2754 + int ret = select(rfd_ + 1, &set, NULL, NULL, &timeout);
2755 +#endif
2756 + if (ret > 0) {
2757 + // Handle potential race condition:
2758 + // - the above check succeeded, i.e. read() should not block
2759 + // - the character disappears before we call read()
2760 + //
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_);
2764 +
2765 + if (dup_rfd_ != -1) {
2766 + struct sigaction act, old_act;
2767 + int ret = 0;
2768 +
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;
2774 +
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) {
2779 + char buf;
2780 +
2781 + // Now try to read() from dup_rfd_. Return values from read():
2782 + //
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);
2791 +
2792 + // disarm timer
2793 + memset(&timeout, 0, sizeof(timeout));
2794 + setitimer(ITIMER_REAL, &timeout, NULL);
2795 + }
2796 +
2797 + sigaction(SIGCHLD, &old_act, NULL);
2798 + }
2799 +
2800 + CloseDupRfd(0);
2801 +
2802 + // Case 1 from above list
2803 + if (ret > 0)
2804 + return true;
2805 + }
2806 + }
2807 +
2808 + // read() would block, i.e. no token available,
2809 + // cases 2-6 from above list or
2810 + // select() / poll() / dup() / sigaction() / setitimer() failed
2811 + return false;
2812 +}
2813 +
2814 +bool GNUmakeTokenPoolPosix::ReturnToken() {
2815 + const char buf = '+';
2816 + while (1) {
2817 + int ret = write(wfd_, &buf, 1);
2818 + if (ret > 0)
2819 + return true;
2820 + if ((ret != -1) || (errno != EINTR))
2821 + return false;
2822 + // write got interrupted - retry
2823 + }
2824 +}
2825 +
2826 +int GNUmakeTokenPoolPosix::GetMonitorFd() {
2827 + return(rfd_);
2828 +}
2829 +
2830 +struct TokenPool *TokenPool::Get() {
2831 + return new GNUmakeTokenPoolPosix;
2832 +}
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
2836 --- /dev/null
2837 +++ b/src/tokenpool-gnu-make-win32.cc
2838 @@ -0,0 +1,237 @@
2839 +// Copyright 2018 Google Inc. All Rights Reserved.
2840 +//
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
2844 +//
2845 +// http://www.apache.org/licenses/LICENSE-2.0
2846 +//
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.
2852 +
2853 +#include "tokenpool-gnu-make.h"
2854 +
2855 +// always include first to make sure other headers do the correct thing...
2856 +#include <windows.h>
2857 +
2858 +#include <ctype.h>
2859 +#include <stdlib.h>
2860 +#include <string.h>
2861 +
2862 +#include "util.h"
2863 +
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();
2869 +
2870 + virtual void WaitForTokenAvailability(HANDLE ioport);
2871 + virtual bool TokenIsAvailable(ULONG_PTR key);
2872 +
2873 + virtual const char *GetEnv(const char *name);
2874 + virtual bool ParseAuth(const char *jobserver);
2875 + virtual bool AcquireToken();
2876 + virtual bool ReturnToken();
2877 +
2878 + private:
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
2890 + bool running_;
2891 + // child thread
2892 + HANDLE child_;
2893 + // I/O completion port from SubprocessSet
2894 + HANDLE ioport_;
2895 +
2896 +
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);
2902 +};
2903 +
2904 +GNUmakeTokenPoolWin32::GNUmakeTokenPoolWin32() : semaphore_jobserver_(NULL),
2905 + semaphore_enter_wait_(NULL),
2906 + semaphore_restart_(NULL),
2907 + running_(false),
2908 + child_(NULL),
2909 + ioport_(NULL) {
2910 +}
2911 +
2912 +GNUmakeTokenPoolWin32::~GNUmakeTokenPoolWin32() {
2913 + Clear();
2914 + CloseHandle(semaphore_jobserver_);
2915 + semaphore_jobserver_ = NULL;
2916 +
2917 + if (child_) {
2918 + // tell child thread to exit
2919 + running_ = false;
2920 + ReleaseSemaphore(semaphore_restart_);
2921 +
2922 + // wait for child thread to exit
2923 + WaitForObject(child_);
2924 + CloseHandle(child_);
2925 + child_ = NULL;
2926 + }
2927 +
2928 + if (semaphore_restart_) {
2929 + CloseHandle(semaphore_restart_);
2930 + semaphore_restart_ = NULL;
2931 + }
2932 +
2933 + if (semaphore_enter_wait_) {
2934 + CloseHandle(semaphore_enter_wait_);
2935 + semaphore_enter_wait_ = NULL;
2936 + }
2937 +}
2938 +
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)
2943 + return NULL;
2944 + return(buffer);
2945 +}
2946 +
2947 +bool GNUmakeTokenPoolWin32::ParseAuth(const char *jobserver) {
2948 + // match "--jobserver-auth=gmake_semaphore_<INTEGER>..."
2949 + const char *start = strchr(jobserver, '=');
2950 + if (start) {
2951 + const char *end = start;
2952 + unsigned int len;
2953 + char c, *auth;
2954 +
2955 + while ((c = *++end) != '\0')
2956 + if (!(isalnum(c) || (c == '_')))
2957 + break;
2958 + len = end - start; // includes string terminator in count
2959 +
2960 + if ((len > 1) && ((auth = (char *)malloc(len)) != NULL)) {
2961 + strncpy(auth, start + 1, len - 1);
2962 + auth[len - 1] = '\0';
2963 +
2964 + if ((semaphore_jobserver_ = OpenSemaphore(SEMAPHORE_ALL_ACCESS, /* Semaphore access setting */
2965 + FALSE, /* Child processes DON'T inherit */
2966 + auth /* Semaphore name */
2967 + )) != NULL) {
2968 + free(auth);
2969 + return true;
2970 + }
2971 +
2972 + free(auth);
2973 + }
2974 + }
2975 +
2976 + return false;
2977 +}
2978 +
2979 +bool GNUmakeTokenPoolWin32::AcquireToken() {
2980 + return WaitForSingleObject(semaphore_jobserver_, 0) == WAIT_OBJECT_0;
2981 +}
2982 +
2983 +bool GNUmakeTokenPoolWin32::ReturnToken() {
2984 + ReleaseSemaphore(semaphore_jobserver_);
2985 + return true;
2986 +}
2987 +
2988 +DWORD GNUmakeTokenPoolWin32::SemaphoreThread() {
2989 + while (running_) {
2990 + // indicate to parent that we are entering wait
2991 + ReleaseSemaphore(semaphore_enter_wait_);
2992 +
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_);
2997 +
2998 + // indicate to parent on ioport that a token might be available
2999 + if (!PostQueuedCompletionStatus(ioport_, 0, (ULONG_PTR) this, NULL))
3000 + Win32Fatal("PostQueuedCompletionStatus");
3001 + }
3002 +
3003 + // wait for parent to allow loop restart
3004 + WaitForObject(semaphore_restart_);
3005 + // semaphore is now in nonsignaled state again for next run...
3006 + }
3007 +
3008 + return 0;
3009 +}
3010 +
3011 +DWORD WINAPI GNUmakeTokenPoolWin32::SemaphoreThreadWrapper(LPVOID param) {
3012 + GNUmakeTokenPoolWin32 *This = (GNUmakeTokenPoolWin32 *) param;
3013 + return This->SemaphoreThread();
3014 +}
3015 +
3016 +void GNUmakeTokenPoolWin32::NoopAPCFunc(ULONG_PTR param) {
3017 +}
3018 +
3019 +void GNUmakeTokenPoolWin32::WaitForTokenAvailability(HANDLE ioport) {
3020 + if (child_ == NULL) {
3021 + // first invocation
3022 + //
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
3027 + ioport_ = ioport;
3028 +
3029 + // create both semaphores in nonsignaled state
3030 + if ((semaphore_enter_wait_ = CreateSemaphore(NULL, 0, 1, NULL))
3031 + == NULL)
3032 + Win32Fatal("CreateSemaphore/enter_wait");
3033 + if ((semaphore_restart_ = CreateSemaphore(NULL, 0, 1, NULL))
3034 + == NULL)
3035 + Win32Fatal("CreateSemaphore/restart");
3036 +
3037 + // start child thread
3038 + running_ = true;
3039 + if ((child_ = CreateThread(NULL, 0, &SemaphoreThreadWrapper, this, 0, NULL))
3040 + == NULL)
3041 + Win32Fatal("CreateThread");
3042 +
3043 + } else {
3044 + // all further invocations - allow child thread to loop
3045 + ReleaseSemaphore(semaphore_restart_);
3046 + }
3047 +
3048 + // wait for child thread to enter wait
3049 + WaitForObject(semaphore_enter_wait_);
3050 + // semaphore is now in nonsignaled state again for next run...
3051 +
3052 + // now SubprocessSet::DoWork() can enter GetQueuedCompletionStatus()...
3053 +}
3054 +
3055 +bool GNUmakeTokenPoolWin32::TokenIsAvailable(ULONG_PTR key) {
3056 + // alert child thread to break wait on token semaphore
3057 + QueueUserAPC(&NoopAPCFunc, child_, (ULONG_PTR)NULL);
3058 +
3059 + // return true when GetQueuedCompletionStatus() returned our key
3060 + return key == (ULONG_PTR) this;
3061 +}
3062 +
3063 +void GNUmakeTokenPoolWin32::ReleaseSemaphore(HANDLE semaphore) {
3064 + if (!::ReleaseSemaphore(semaphore, 1, NULL))
3065 + Win32Fatal("ReleaseSemaphore");
3066 +}
3067 +
3068 +void GNUmakeTokenPoolWin32::WaitForObject(HANDLE object) {
3069 + if (WaitForSingleObject(object, INFINITE) != WAIT_OBJECT_0)
3070 + Win32Fatal("WaitForSingleObject");
3071 +}
3072 +
3073 +struct TokenPool *TokenPool::Get() {
3074 + return new GNUmakeTokenPoolWin32;
3075 +}
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.
3083
3084 -#include "tokenpool.h"
3085 +#include "tokenpool-gnu-make.h"
3086
3087 -#include <errno.h>
3088 -#include <fcntl.h>
3089 -#include <poll.h>
3090 -#include <unistd.h>
3091 -#include <signal.h>
3092 -#include <sys/time.h>
3093 +#include <stdlib.h>
3094 #include <stdio.h>
3095 #include <string.h>
3096 -#include <stdlib.h>
3097
3098 #include "line_printer.h"
3099
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();
3105 -
3106 - virtual bool Acquire();
3107 - virtual void Reserve();
3108 - virtual void Release();
3109 - virtual void Clear();
3110 - virtual int GetMonitorFd();
3111 -
3112 - bool Setup(bool ignore, bool verbose, double& max_load_average);
3113 -
3114 - private:
3115 - int available_;
3116 - int used_;
3117 -
3118 -#ifdef _WIN32
3119 - // @TODO
3120 -#else
3121 - int rfd_;
3122 - int wfd_;
3123 -
3124 - struct sigaction old_act_;
3125 - bool restore_;
3126 -
3127 - static int dup_rfd_;
3128 - static void CloseDupRfd(int signum);
3129 -
3130 - bool CheckFd(int fd);
3131 - bool SetAlarmHandler();
3132 -#endif
3133 -
3134 - void Return();
3135 -};
3136 -
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) {
3142 }
3143
3144 GNUmakeTokenPool::~GNUmakeTokenPool() {
3145 - Clear();
3146 - if (restore_)
3147 - sigaction(SIGALRM, &old_act_, NULL);
3148 -}
3149 -
3150 -bool GNUmakeTokenPool::CheckFd(int fd) {
3151 - if (fd < 0)
3152 - return false;
3153 - int ret = fcntl(fd, F_GETFD);
3154 - if (ret < 0)
3155 - return false;
3156 - return true;
3157 -}
3158 -
3159 -int GNUmakeTokenPool::dup_rfd_ = -1;
3160 -
3161 -void GNUmakeTokenPool::CloseDupRfd(int signum) {
3162 - close(dup_rfd_);
3163 - dup_rfd_ = -1;
3164 -}
3165 -
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:");
3172 - return(false);
3173 - } else {
3174 - restore_ = true;
3175 - return(true);
3176 - }
3177 }
3178
3179 bool GNUmakeTokenPool::Setup(bool ignore,
3180 bool verbose,
3181 double& max_load_average) {
3182 - const char *value = getenv("MAKEFLAGS");
3183 + const char *value = GetEnv("MAKEFLAGS");
3184 if (value) {
3185 // GNU make <= 4.1
3186 const char *jobserver = strstr(value, "--jobserver-fds=");
3187 @@ -119,20 +44,13 @@ bool GNUmakeTokenPool::Setup(bool ignore,
3188 if (ignore) {
3189 printer.PrintOnNewLine("ninja: warning: -jN forced on command line; ignoring GNU make jobserver.\n");
3190 } else {
3191 - int rfd = -1;
3192 - int wfd = -1;
3193 - if ((sscanf(jobserver, "%*[^=]=%d,%d", &rfd, &wfd) == 2) &&
3194 - CheckFd(rfd) &&
3195 - CheckFd(wfd) &&
3196 - SetAlarmHandler()) {
3197 + if (ParseAuth(jobserver)) {
3198 const char *l_arg = strstr(value, " -l");
3199 int load_limit = -1;
3200
3201 if (verbose) {
3202 printer.PrintOnNewLine("ninja: using GNU make jobserver.\n");
3203 }
3204 - rfd_ = rfd;
3205 - wfd_ = wfd;
3206
3207 // translate GNU make -lN to ninja -lN
3208 if (l_arg &&
3209 @@ -154,83 +72,14 @@ bool GNUmakeTokenPool::Acquire() {
3210 if (available_ > 0)
3211 return true;
3212
3213 - // Please read
3214 - //
3215 - // http://make.mad-scientist.net/papers/jobserver-implementation/
3216 - //
3217 - // for the reasoning behind the following code.
3218 - //
3219 - // Try to read one character from the pipe. Returns true on success.
3220 - //
3221 - // First check if read() would succeed without blocking.
3222 -#ifdef USE_PPOLL
3223 - pollfd pollfds[] = {{rfd_, POLLIN, 0}};
3224 - int ret = poll(pollfds, 1, 0);
3225 -#else
3226 - fd_set set;
3227 - struct timeval timeout = { 0, 0 };
3228 - FD_ZERO(&set);
3229 - FD_SET(rfd_, &set);
3230 - int ret = select(rfd_ + 1, &set, NULL, NULL, &timeout);
3231 -#endif
3232 - if (ret > 0) {
3233 - // Handle potential race condition:
3234 - // - the above check succeeded, i.e. read() should not block
3235 - // - the character disappears before we call read()
3236 - //
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_);
3240 -
3241 - if (dup_rfd_ != -1) {
3242 - struct sigaction act, old_act;
3243 - int ret = 0;
3244 -
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;
3250 -
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) {
3255 - char buf;
3256 -
3257 - // Now try to read() from dup_rfd_. Return values from read():
3258 - //
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);
3267 -
3268 - // disarm timer
3269 - memset(&timeout, 0, sizeof(timeout));
3270 - setitimer(ITIMER_REAL, &timeout, NULL);
3271 - }
3272 -
3273 - sigaction(SIGCHLD, &old_act, NULL);
3274 - }
3275 -
3276 - CloseDupRfd(0);
3277 -
3278 - // Case 1 from above list
3279 - if (ret > 0) {
3280 - available_++;
3281 - return true;
3282 - }
3283 - }
3284 + if (AcquireToken()) {
3285 + // token acquired
3286 + available_++;
3287 + return true;
3288 + } else {
3289 + // no token available
3290 + return false;
3291 }
3292 -
3293 - // read() would block, i.e. no token available,
3294 - // cases 2-6 from above list or
3295 - // select() / poll() / dup() / sigaction() / setitimer() failed
3296 - return false;
3297 }
3298
3299 void GNUmakeTokenPool::Reserve() {
3300 @@ -239,15 +88,8 @@ void GNUmakeTokenPool::Reserve() {
3301 }
3302
3303 void GNUmakeTokenPool::Return() {
3304 - const char buf = '+';
3305 - while (1) {
3306 - int ret = write(wfd_, &buf, 1);
3307 - if (ret > 0)
3308 - available_--;
3309 - if ((ret != -1) || (errno != EINTR))
3310 - return;
3311 - // write got interrupted - retry
3312 - }
3313 + if (ReturnToken())
3314 + available_--;
3315 }
3316
3317 void GNUmakeTokenPool::Release() {
3318 @@ -263,18 +105,3 @@ void GNUmakeTokenPool::Clear() {
3319 while (available_ > 1)
3320 Return();
3321 }
3322 -
3323 -int GNUmakeTokenPool::GetMonitorFd() {
3324 - return(rfd_);
3325 -}
3326 -
3327 -struct TokenPool *TokenPool::Get(bool ignore,
3328 - bool verbose,
3329 - double& max_load_average) {
3330 - GNUmakeTokenPool *tokenpool = new GNUmakeTokenPool;
3331 - if (tokenpool->Setup(ignore, verbose, max_load_average))
3332 - return tokenpool;
3333 - else
3334 - delete tokenpool;
3335 - return NULL;
3336 -}
3337 diff --git a/src/tokenpool-gnu-make.h b/src/tokenpool-gnu-make.h
3338 new file mode 100644
3339 index 0000000000..d3852088e2
3340 --- /dev/null
3341 +++ b/src/tokenpool-gnu-make.h
3342 @@ -0,0 +1,40 @@
3343 +// Copyright 2016-2018 Google Inc. All Rights Reserved.
3344 +//
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
3348 +//
3349 +// http://www.apache.org/licenses/LICENSE-2.0
3350 +//
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.
3356 +
3357 +#include "tokenpool.h"
3358 +
3359 +// interface to GNU make token pool
3360 +struct GNUmakeTokenPool : public TokenPool {
3361 + GNUmakeTokenPool();
3362 + virtual ~GNUmakeTokenPool();
3363 +
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);
3370 +
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;
3376 +
3377 + private:
3378 + int available_;
3379 + int used_;
3380 +
3381 + void Return();
3382 +};
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
3387 @@ -17,8 +17,6 @@
3388 #include <stdlib.h>
3389
3390 // No-op TokenPool implementation
3391 -struct TokenPool *TokenPool::Get(bool ignore,
3392 - bool verbose,
3393 - double& max_load_average) {
3394 +struct TokenPool *TokenPool::Get() {
3395 return NULL;
3396 }
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
3401 @@ -1,4 +1,4 @@
3402 -// Copyright 2016-2017 Google Inc. All Rights Reserved.
3403 +// Copyright 2016-2018 Google Inc. All Rights Reserved.
3404 //
3405 // Licensed under the Apache License, Version 2.0 (the "License");
3406 // you may not use this file except in compliance with the License.
3407 @@ -12,6 +12,10 @@
3408 // See the License for the specific language governing permissions and
3409 // limitations under the License.
3410
3411 +#ifdef _WIN32
3412 +#include <windows.h>
3413 +#endif
3414 +
3415 // interface to token pool
3416 struct TokenPool {
3417 virtual ~TokenPool() {}
3418 @@ -21,14 +25,18 @@ struct TokenPool {
3419 virtual void Release() = 0;
3420 virtual void Clear() = 0;
3421
3422 + // returns false if token pool setup failed
3423 + virtual bool Setup(bool ignore, bool verbose, double& max_load_average) = 0;
3424 +
3425 #ifdef _WIN32
3426 - // @TODO
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;
3431 #else
3432 virtual int GetMonitorFd() = 0;
3433 #endif
3434
3435 // returns NULL if token pool is not available
3436 - static struct TokenPool *Get(bool ignore,
3437 - bool verbose,
3438 - double& max_load_average);
3439 + static struct TokenPool *Get();
3440 };
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
3445 @@ -16,13 +16,25 @@
3446
3447 #include "test.h"
3448
3449 -#ifndef _WIN32
3450 +#ifdef _WIN32
3451 +#include <windows.h>
3452 +#else
3453 +#include <unistd.h>
3454 +#endif
3455 +
3456 #include <stdio.h>
3457 #include <stdlib.h>
3458 -#include <unistd.h>
3459
3460 +#ifdef _WIN32
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)
3466 +#else
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)
3471 #endif
3472
3473 namespace {
3474 @@ -32,43 +44,60 @@ const double kLoadAverageDefault = -1.23456789;
3475 struct TokenPoolTest : public testing::Test {
3476 double load_avg_;
3477 TokenPool *tokens_;
3478 -#ifndef _WIN32
3479 char buf_[1024];
3480 +#ifdef _WIN32
3481 + const char *semaphore_name_;
3482 + HANDLE semaphore_;
3483 +#else
3484 int fds_[2];
3485 #endif
3486
3487 virtual void SetUp() {
3488 load_avg_ = kLoadAverageDefault;
3489 tokens_ = NULL;
3490 -#ifndef _WIN32
3491 ENVIRONMENT_CLEAR();
3492 +#ifdef _WIN32
3493 + semaphore_name_ = SEMAPHORE_NAME;
3494 + if ((semaphore_ = CreateSemaphore(0, 0, 2, SEMAPHORE_NAME)) == NULL)
3495 +#else
3496 if (pipe(fds_) < 0)
3497 - ASSERT_TRUE(false);
3498 #endif
3499 + ASSERT_TRUE(false);
3500 }
3501
3502 - void CreatePool(const char *format, bool ignore_jobserver) {
3503 -#ifndef _WIN32
3504 + void CreatePool(const char *format, bool ignore_jobserver = false) {
3505 if (format) {
3506 - sprintf(buf_, format, fds_[0], fds_[1]);
3507 + sprintf(buf_, format,
3508 +#ifdef _WIN32
3509 + semaphore_name_
3510 +#else
3511 + fds_[0], fds_[1]
3512 +#endif
3513 + );
3514 ENVIRONMENT_INIT(buf_);
3515 }
3516 -#endif
3517 - tokens_ = TokenPool::Get(ignore_jobserver, false, load_avg_);
3518 + if ((tokens_ = TokenPool::Get()) != NULL) {
3519 + if (!tokens_->Setup(ignore_jobserver, false, load_avg_)) {
3520 + delete tokens_;
3521 + tokens_ = NULL;
3522 + }
3523 + }
3524 }
3525
3526 void CreateDefaultPool() {
3527 - CreatePool("foo --jobserver-auth=%d,%d bar", false);
3528 + CreatePool(AUTH_FORMAT("--jobserver-auth"));
3529 }
3530
3531 virtual void TearDown() {
3532 if (tokens_)
3533 delete tokens_;
3534 -#ifndef _WIN32
3535 +#ifdef _WIN32
3536 + CloseHandle(semaphore_);
3537 +#else
3538 close(fds_[0]);
3539 close(fds_[1]);
3540 - ENVIRONMENT_CLEAR();
3541 #endif
3542 + ENVIRONMENT_CLEAR();
3543 }
3544 };
3545
3546 @@ -82,10 +111,9 @@ TEST_F(TokenPoolTest, NoTokenPool) {
3547 EXPECT_EQ(kLoadAverageDefault, load_avg_);
3548 }
3549
3550 -#ifndef _WIN32
3551 TEST_F(TokenPoolTest, SuccessfulOldSetup) {
3552 // GNUmake <= 4.1
3553 - CreatePool("foo --jobserver-fds=%d,%d bar", false);
3554 + CreatePool(AUTH_FORMAT("--jobserver-fds"));
3555
3556 EXPECT_NE(NULL, tokens_);
3557 EXPECT_EQ(kLoadAverageDefault, load_avg_);
3558 @@ -100,19 +128,37 @@ TEST_F(TokenPoolTest, SuccessfulNewSetup) {
3559 }
3560
3561 TEST_F(TokenPoolTest, IgnoreWithJN) {
3562 - CreatePool("foo --jobserver-auth=%d,%d bar", true);
3563 + CreatePool(AUTH_FORMAT("--jobserver-auth"), true);
3564
3565 EXPECT_EQ(NULL, tokens_);
3566 EXPECT_EQ(kLoadAverageDefault, load_avg_);
3567 }
3568
3569 TEST_F(TokenPoolTest, HonorLN) {
3570 - CreatePool("foo -l9 --jobserver-auth=%d,%d bar", false);
3571 + CreatePool(AUTH_FORMAT("-l9 --jobserver-auth"));
3572
3573 EXPECT_NE(NULL, tokens_);
3574 EXPECT_EQ(9.0, load_avg_);
3575 }
3576
3577 +#ifdef _WIN32
3578 +TEST_F(TokenPoolTest, SemaphoreNotFound) {
3579 + semaphore_name_ = SEMAPHORE_NAME "_foobar";
3580 + CreateDefaultPool();
3581 +
3582 + EXPECT_EQ(NULL, tokens_);
3583 + EXPECT_EQ(kLoadAverageDefault, load_avg_);
3584 +}
3585 +
3586 +TEST_F(TokenPoolTest, TokenIsAvailable) {
3587 + CreateDefaultPool();
3588 +
3589 + ASSERT_NE(NULL, tokens_);
3590 + EXPECT_EQ(kLoadAverageDefault, load_avg_);
3591 +
3592 + EXPECT_TRUE(tokens_->TokenIsAvailable((ULONG_PTR)tokens_));
3593 +}
3594 +#else
3595 TEST_F(TokenPoolTest, MonitorFD) {
3596 CreateDefaultPool();
3597
3598 @@ -121,6 +167,7 @@ TEST_F(TokenPoolTest, MonitorFD) {
3599
3600 EXPECT_EQ(fds_[0], tokens_->GetMonitorFd());
3601 }
3602 +#endif
3603
3604 TEST_F(TokenPoolTest, ImplicitToken) {
3605 CreateDefaultPool();
3606 @@ -147,7 +194,13 @@ TEST_F(TokenPoolTest, TwoTokens) {
3607 EXPECT_FALSE(tokens_->Acquire());
3608
3609 // jobserver offers 2nd token
3610 +#ifdef _WIN32
3611 + LONG previous;
3612 + ASSERT_TRUE(ReleaseSemaphore(semaphore_, 1, &previous));
3613 + ASSERT_EQ(0, previous);
3614 +#else
3615 ASSERT_EQ(1u, write(fds_[1], "T", 1));
3616 +#endif
3617 EXPECT_TRUE(tokens_->Acquire());
3618 tokens_->Reserve();
3619 EXPECT_FALSE(tokens_->Acquire());
3620 @@ -160,8 +213,14 @@ TEST_F(TokenPoolTest, TwoTokens) {
3621 tokens_->Release();
3622 EXPECT_TRUE(tokens_->Acquire());
3623
3624 - // there must be one token in the pipe
3625 + // there must be one token available
3626 +#ifdef _WIN32
3627 + EXPECT_EQ(WAIT_OBJECT_0, WaitForSingleObject(semaphore_, 0));
3628 + EXPECT_TRUE(ReleaseSemaphore(semaphore_, 1, &previous));
3629 + EXPECT_EQ(0, previous);
3630 +#else
3631 EXPECT_EQ(1u, read(fds_[0], buf_, sizeof(buf_)));
3632 +#endif
3633
3634 // implicit token
3635 EXPECT_TRUE(tokens_->Acquire());
3636 @@ -179,7 +238,13 @@ TEST_F(TokenPoolTest, Clear) {
3637 EXPECT_FALSE(tokens_->Acquire());
3638
3639 // jobserver offers 2nd & 3rd token
3640 +#ifdef _WIN32
3641 + LONG previous;
3642 + ASSERT_TRUE(ReleaseSemaphore(semaphore_, 2, &previous));
3643 + ASSERT_EQ(0, previous);
3644 +#else
3645 ASSERT_EQ(2u, write(fds_[1], "TT", 2));
3646 +#endif
3647 EXPECT_TRUE(tokens_->Acquire());
3648 tokens_->Reserve();
3649 EXPECT_TRUE(tokens_->Acquire());
3650 @@ -189,10 +254,16 @@ TEST_F(TokenPoolTest, Clear) {
3651 tokens_->Clear();
3652 EXPECT_TRUE(tokens_->Acquire());
3653
3654 - // there must be two tokens in the pipe
3655 + // there must be two tokens available
3656 +#ifdef _WIN32
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);
3661 +#else
3662 EXPECT_EQ(2u, read(fds_[0], buf_, sizeof(buf_)));
3663 +#endif
3664
3665 // implicit token
3666 EXPECT_TRUE(tokens_->Acquire());
3667 }
3668 -#endif
3669
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
3674
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
3684
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
3689 ---
3690 CMakeLists.txt | 8 ++++-
3691 src/build.cc | 2 +-
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
3706
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
3712 src/state.cc
3713 src/status.cc
3714 src/string_piece_util.cc
3715 + src/tokenpool-gnu-make.cc
3716 src/util.cc
3717 src/version.cc
3718 )
3719 @@ -123,9 +124,13 @@ if(WIN32)
3720 src/msvc_helper_main-win32.cc
3721 src/getopt.c
3722 src/minidump-win32.cc
3723 + src/tokenpool-gnu-make-win32.cc
3724 )
3725 else()
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
3730 + )
3731 if(CMAKE_SYSTEM_NAME STREQUAL "OS400" OR CMAKE_SYSTEM_NAME STREQUAL "AIX")
3732 target_sources(libninja PRIVATE src/getopt.c)
3733 endif()
3734 @@ -204,6 +209,7 @@ if(BUILD_TESTING)
3735 src/string_piece_util_test.cc
3736 src/subprocess_test.cc
3737 src/test.cc
3738 + src/tokenpool_test.cc
3739 src/util_test.cc
3740 )
3741 if(WIN32)
3742 diff --git a/src/build.cc b/src/build.cc
3743 index 20c3bdc2a0..854df08c2a 100644
3744 --- a/src/build.cc
3745 +++ b/src/build.cc
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_;
3753 };
3754
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, ...);
3761
3762 private:
3763 - void EnqueueBooleans(vector<bool>& booleans, int count, va_list ao);
3764 + void EnqueueBooleans(vector<bool>& booleans, int count, va_list ap);
3765 };
3766
3767 void BuildTokenTest::SetUp() {
3768 @@ -4144,16 +4144,6 @@ void BuildTokenTest::EnqueueBooleans(vector<bool>& booleans, int count, va_list
3769 }
3770 }
3771
3772 -TEST_F(BuildTokenTest, CompleteNoWork) {
3773 - // plan should not execute anything
3774 - string err;
3775 -
3776 - EXPECT_TRUE(builder_.Build(&err));
3777 - EXPECT_EQ("", err);
3778 -
3779 - EXPECT_EQ(0u, token_command_runner_.commands_ran_.size());
3780 -}
3781 -
3782 TEST_F(BuildTokenTest, DoNotAquireToken) {
3783 // plan should execute one command
3784 string err;
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) {
3790 }
3791
3792 #ifdef USE_PPOLL
3793 -bool SubprocessSet::DoWork(struct TokenPool* tokens) {
3794 +bool SubprocessSet::DoWork(TokenPool* tokens) {
3795 vector<pollfd> fds;
3796 nfds_t nfds = 0;
3797
3798 @@ -315,7 +315,7 @@ bool SubprocessSet::DoWork(struct TokenPool* tokens) {
3799 }
3800
3801 #else // !defined(USE_PPOLL)
3802 -bool SubprocessSet::DoWork(struct TokenPool* tokens) {
3803 +bool SubprocessSet::DoWork(TokenPool* tokens) {
3804 fd_set set;
3805 int nfds = 0;
3806 FD_ZERO(&set);
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) {
3812 return subprocess;
3813 }
3814
3815 -bool SubprocessSet::DoWork(struct TokenPool* tokens) {
3816 +bool SubprocessSet::DoWork(TokenPool* tokens) {
3817 DWORD bytes_read;
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 {
3825 ~SubprocessSet();
3826
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();
3831 void Clear();
3832
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 /";
3839 #endif
3840
3841 -struct TokenPoolTest : public TokenPool {
3842 +struct TestTokenPool : public TokenPool {
3843 bool Acquire() { return false; }
3844 void Reserve() {}
3845 void Release() {}
3846 @@ -58,7 +58,7 @@ struct TokenPoolTest : public TokenPool {
3847
3848 struct SubprocessTest : public testing::Test {
3849 SubprocessSet subprocs_;
3850 - TokenPoolTest tokens_;
3851 + TestTokenPool tokens_;
3852 };
3853
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);
3858 }
3859 - ASSERT_EQ(false, subprocs_.IsTokenAvailable());
3860 + ASSERT_FALSE(subprocs_.IsTokenAvailable());
3861
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);
3867 }
3868 - ASSERT_EQ(false, subprocs_.IsTokenAvailable());
3869 + ASSERT_FALSE(subprocs_.IsTokenAvailable());
3870
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);
3876 }
3877 - ASSERT_EQ(false, subprocs_.IsTokenAvailable());
3878 + ASSERT_FALSE(subprocs_.IsTokenAvailable());
3879
3880 EXPECT_EQ(ExitInterrupted, subproc->Finish());
3881 }
3882 @@ -135,7 +135,7 @@ TEST_F(SubprocessTest, InterruptChildWithSigTerm) {
3883 while (!subproc->Done()) {
3884 subprocs_.DoWork(NULL);
3885 }
3886 - ASSERT_EQ(false, subprocs_.IsTokenAvailable());
3887 + ASSERT_FALSE(subprocs_.IsTokenAvailable());
3888
3889 EXPECT_EQ(ExitInterrupted, subproc->Finish());
3890 }
3891 @@ -161,7 +161,7 @@ TEST_F(SubprocessTest, InterruptChildWithSigHup) {
3892 while (!subproc->Done()) {
3893 subprocs_.DoWork(NULL);
3894 }
3895 - ASSERT_EQ(false, subprocs_.IsTokenAvailable());
3896 + ASSERT_FALSE(subprocs_.IsTokenAvailable());
3897
3898 EXPECT_EQ(ExitInterrupted, subproc->Finish());
3899 }
3900 @@ -190,7 +190,7 @@ TEST_F(SubprocessTest, Console) {
3901 while (!subproc->Done()) {
3902 subprocs_.DoWork(NULL);
3903 }
3904 - ASSERT_EQ(false, subprocs_.IsTokenAvailable());
3905 + ASSERT_FALSE(subprocs_.IsTokenAvailable());
3906
3907 EXPECT_EQ(ExitSuccess, subproc->Finish());
3908 }
3909 @@ -206,7 +206,7 @@ TEST_F(SubprocessTest, SetWithSingle) {
3910 while (!subproc->Done()) {
3911 subprocs_.DoWork(NULL);
3912 }
3913 - ASSERT_EQ(false, subprocs_.IsTokenAvailable());
3914 + ASSERT_FALSE(subprocs_.IsTokenAvailable());
3915 ASSERT_EQ(ExitSuccess, subproc->Finish());
3916 ASSERT_NE("", subproc->GetOutput());
3917
3918 @@ -243,7 +243,7 @@ TEST_F(SubprocessTest, SetWithMulti) {
3919 ASSERT_GT(subprocs_.running_.size(), 0u);
3920 subprocs_.DoWork(NULL);
3921 }
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());
3926
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);
3939 }
3940 - ASSERT_EQ(false, subprocs_.IsTokenAvailable());
3941 + ASSERT_FALSE(subprocs_.IsTokenAvailable());
3942 ASSERT_EQ(ExitSuccess, subproc->Finish());
3943 ASSERT_EQ(1u, subprocs_.finished_.size());
3944 }
3945 @@ -322,7 +322,7 @@ TEST_F(SubprocessTest, TokenAvailable) {
3946 subprocs_.DoWork(&tokens_);
3947 #ifdef _WIN32
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 {
3959
3960 virtual int GetMonitorFd();
3961
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();
3968
3969 @@ -64,9 +64,7 @@ bool GNUmakeTokenPoolPosix::CheckFd(int fd) {
3970 if (fd < 0)
3971 return false;
3972 int ret = fcntl(fd, F_GETFD);
3973 - if (ret < 0)
3974 - return false;
3975 - return true;
3976 + return ret >= 0;
3977 }
3978
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:");
3984 - return(false);
3985 - } else {
3986 - restore_ = true;
3987 - return(true);
3988 + return false;
3989 }
3990 + restore_ = true;
3991 + return true;
3992 }
3993
3994 -bool GNUmakeTokenPoolPosix::ParseAuth(const char *jobserver) {
3995 +bool GNUmakeTokenPoolPosix::ParseAuth(const char* jobserver) {
3996 int rfd = -1;
3997 int wfd = -1;
3998 if ((sscanf(jobserver, "%*[^=]=%d,%d", &rfd, &wfd) == 2) &&
3999 @@ -195,9 +192,9 @@ bool GNUmakeTokenPoolPosix::ReturnToken() {
4000 }
4001
4002 int GNUmakeTokenPoolPosix::GetMonitorFd() {
4003 - return(rfd_);
4004 + return rfd_;
4005 }
4006
4007 -struct TokenPool *TokenPool::Get() {
4008 +TokenPool* TokenPool::Get() {
4009 return new GNUmakeTokenPoolPosix;
4010 }
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
4015 @@ -14,7 +14,8 @@
4016
4017 #include "tokenpool-gnu-make.h"
4018
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>
4023
4024 #include <ctype.h>
4025 @@ -32,8 +33,8 @@ struct GNUmakeTokenPoolWin32 : public GNUmakeTokenPool {
4026 virtual void WaitForTokenAvailability(HANDLE ioport);
4027 virtual bool TokenIsAvailable(ULONG_PTR key);
4028
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();
4035
4036 @@ -98,19 +99,19 @@ GNUmakeTokenPoolWin32::~GNUmakeTokenPoolWin32() {
4037 }
4038 }
4039
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)
4046 return NULL;
4047 - return(buffer);
4048 + return buffer;
4049 }
4050
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, '=');
4056 if (start) {
4057 - const char *end = start;
4058 + const char* end = start;
4059 unsigned int len;
4060 char c, *auth;
4061
4062 @@ -119,14 +120,15 @@ bool GNUmakeTokenPoolWin32::ParseAuth(const char *jobserver) {
4063 break;
4064 len = end - start; // includes string terminator in count
4065
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';
4070
4071 - if ((semaphore_jobserver_ = OpenSemaphore(SEMAPHORE_ALL_ACCESS, /* Semaphore access setting */
4072 - FALSE, /* Child processes DON'T inherit */
4073 - auth /* Semaphore name */
4074 - )) != NULL) {
4075 + if ((semaphore_jobserver_ =
4076 + OpenSemaphore(SEMAPHORE_ALL_ACCESS, /* Semaphore access setting */
4077 + FALSE, /* Child processes DON'T inherit */
4078 + auth /* Semaphore name */
4079 + )) != NULL) {
4080 free(auth);
4081 return true;
4082 }
4083 @@ -171,7 +173,7 @@ DWORD GNUmakeTokenPoolWin32::SemaphoreThread() {
4084 }
4085
4086 DWORD WINAPI GNUmakeTokenPoolWin32::SemaphoreThreadWrapper(LPVOID param) {
4087 - GNUmakeTokenPoolWin32 *This = (GNUmakeTokenPoolWin32 *) param;
4088 + GNUmakeTokenPoolWin32* This = (GNUmakeTokenPoolWin32*) param;
4089 return This->SemaphoreThread();
4090 }
4091
4092 @@ -216,7 +218,7 @@ void GNUmakeTokenPoolWin32::WaitForTokenAvailability(HANDLE ioport) {
4093
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);
4098
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");
4103 }
4104
4105 -struct TokenPool *TokenPool::Get() {
4106 +TokenPool* TokenPool::Get() {
4107 return new GNUmakeTokenPoolWin32;
4108 }
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,
4115 bool verbose,
4116 double& max_load_average) {
4117 - const char *value = GetEnv("MAKEFLAGS");
4118 - if (value) {
4119 - // GNU make <= 4.1
4120 - const char *jobserver = strstr(value, "--jobserver-fds=");
4121 + const char* value = GetEnv("MAKEFLAGS");
4122 + if (!value)
4123 + return false;
4124 +
4125 + // GNU make <= 4.1
4126 + const char* jobserver = strstr(value, "--jobserver-fds=");
4127 + if (!jobserver)
4128 // GNU make => 4.2
4129 - if (!jobserver)
4130 - jobserver = strstr(value, "--jobserver-auth=");
4131 - if (jobserver) {
4132 - LinePrinter printer;
4133 -
4134 - if (ignore) {
4135 - printer.PrintOnNewLine("ninja: warning: -jN forced on command line; ignoring GNU make jobserver.\n");
4136 - } else {
4137 - if (ParseAuth(jobserver)) {
4138 - const char *l_arg = strstr(value, " -l");
4139 - int load_limit = -1;
4140 -
4141 - if (verbose) {
4142 - printer.PrintOnNewLine("ninja: using GNU make jobserver.\n");
4143 - }
4144 -
4145 - // translate GNU make -lN to ninja -lN
4146 - if (l_arg &&
4147 - (sscanf(l_arg + 3, "%d ", &load_limit) == 1) &&
4148 - (load_limit > 0)) {
4149 - max_load_average = load_limit;
4150 - }
4151 -
4152 - return true;
4153 + jobserver = strstr(value, "--jobserver-auth=");
4154 + if (jobserver) {
4155 + LinePrinter printer;
4156 +
4157 + if (ignore) {
4158 + printer.PrintOnNewLine("ninja: warning: -jN forced on command line; ignoring GNU make jobserver.\n");
4159 + } else {
4160 + if (ParseAuth(jobserver)) {
4161 + const char* l_arg = strstr(value, " -l");
4162 + int load_limit = -1;
4163 +
4164 + if (verbose) {
4165 + printer.PrintOnNewLine("ninja: using GNU make jobserver.\n");
4166 + }
4167 +
4168 + // translate GNU make -lN to ninja -lN
4169 + if (l_arg &&
4170 + (sscanf(l_arg + 3, "%d ", &load_limit) == 1) &&
4171 + (load_limit > 0)) {
4172 + max_load_average = load_limit;
4173 }
4174 +
4175 + return true;
4176 }
4177 }
4178 }
4179 @@ -76,10 +77,10 @@ bool GNUmakeTokenPool::Acquire() {
4180 // token acquired
4181 available_++;
4182 return true;
4183 - } else {
4184 - // no token available
4185 - return false;
4186 }
4187 +
4188 + // no token available
4189 + return false;
4190 }
4191
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
4197 @@ -17,7 +17,7 @@
4198 // interface to GNU make token pool
4199 struct GNUmakeTokenPool : public TokenPool {
4200 GNUmakeTokenPool();
4201 - virtual ~GNUmakeTokenPool();
4202 + ~GNUmakeTokenPool();
4203
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);
4208
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;
4216
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
4221 +++ /dev/null
4222 @@ -1,22 +0,0 @@
4223 -// Copyright 2016-2018 Google Inc. All Rights Reserved.
4224 -//
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
4228 -//
4229 -// http://www.apache.org/licenses/LICENSE-2.0
4230 -//
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.
4236 -
4237 -#include "tokenpool.h"
4238 -
4239 -#include <stdlib.h>
4240 -
4241 -// No-op TokenPool implementation
4242 -struct TokenPool *TokenPool::Get() {
4243 - return NULL;
4244 -}
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 {
4250 #endif
4251
4252 // returns NULL if token pool is not available
4253 - static struct TokenPool *Get();
4254 + static TokenPool* Get();
4255 };
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;
4261
4262 struct TokenPoolTest : public testing::Test {
4263 double load_avg_;
4264 - TokenPool *tokens_;
4265 + TokenPool* tokens_;
4266 char buf_[1024];
4267 #ifdef _WIN32
4268 - const char *semaphore_name_;
4269 + const char* semaphore_name_;
4270 HANDLE semaphore_;
4271 #else
4272 int fds_[2];
4273 @@ -65,7 +65,7 @@ struct TokenPoolTest : public testing::Test {
4274 ASSERT_TRUE(false);
4275 }
4276
4277 - void CreatePool(const char *format, bool ignore_jobserver = false) {
4278 + void CreatePool(const char* format, bool ignore_jobserver = false) {
4279 if (format) {
4280 sprintf(buf_, format,
4281 #ifdef _WIN32
4282 @@ -209,7 +209,7 @@ TEST_F(TokenPoolTest, TwoTokens) {
4283 tokens_->Release();
4284 EXPECT_TRUE(tokens_->Acquire());
4285
4286 - // release implict token - must return 2nd token back to jobserver
4287 + // release implicit token - must return 2nd token back to jobserver
4288 tokens_->Release();
4289 EXPECT_TRUE(tokens_->Acquire());
4290