blob: 82ecf0222ef64b0ac156bfb1f3bd3e8a13a9af7d [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001From afec30f5caf4b051827ffdd822ebd27c58219fee Mon Sep 17 00:00:00 2001
2From: Stefan Becker <stefanb@gpartner-nvidia.com>
3Date: Tue, 22 Mar 2016 13:48:07 +0200
4Subject: [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
17Documentation for GNU make jobserver
18
19 http://make.mad-scientist.net/papers/jobserver-implementation/
20
21--- a/configure.py
22+++ b/configure.py
23@@ -543,11 +543,13 @@ for name in ['build',
24 'state',
25 'status',
26 'string_piece_util',
27+ 'tokenpool-gnu-make',
28 'util',
29 'version']:
30 objs += cxx(name, variables=cxxvariables)
31 if platform.is_windows():
32 for name in ['subprocess-win32',
33+ 'tokenpool-gnu-make-win32',
34 'includes_normalize-win32',
35 'msvc_helper-win32',
36 'msvc_helper_main-win32']:
37@@ -556,7 +558,9 @@ if platform.is_windows():
38 objs += cxx('minidump-win32', variables=cxxvariables)
39 objs += cc('getopt')
40 else:
41- objs += cxx('subprocess-posix')
42+ for name in ['subprocess-posix',
43+ 'tokenpool-gnu-make-posix']:
44+ objs += cxx(name)
45 if platform.is_aix():
46 objs += cc('getopt')
47 if platform.is_msvc():
48@@ -639,6 +643,7 @@ if gtest_src_dir:
49 'string_piece_util_test',
50 'subprocess_test',
51 'test',
52+ 'tokenpool_test',
53 'util_test',
54 ]
55 if platform.is_windows():
56--- a/src/build.cc
57+++ b/src/build.cc
58@@ -39,6 +39,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@@ -50,24 +51,29 @@ struct DryRunCommandRunner : public Comm
67 virtual ~DryRunCommandRunner() {}
68
69 // Overridden from CommandRunner:
70- virtual size_t CanRunMore() const;
71+ virtual size_t CanRunMore();
72+ virtual bool AcquireToken();
73 virtual bool StartCommand(Edge* edge);
74- virtual bool WaitForCommand(Result* result);
75+ virtual bool WaitForCommand(Result* result, bool more_ready);
76
77 private:
78 queue<Edge*> finished_;
79 };
80
81-size_t DryRunCommandRunner::CanRunMore() const {
82+size_t DryRunCommandRunner::CanRunMore() {
83 return SIZE_MAX;
84 }
85
86+bool DryRunCommandRunner::AcquireToken() {
87+ return true;
88+}
89+
90 bool DryRunCommandRunner::StartCommand(Edge* edge) {
91 finished_.push(edge);
92 return true;
93 }
94
95-bool DryRunCommandRunner::WaitForCommand(Result* result) {
96+bool DryRunCommandRunner::WaitForCommand(Result* result, bool more_ready) {
97 if (finished_.empty())
98 return false;
99
100@@ -160,7 +166,7 @@ void Plan::EdgeWanted(const Edge* edge)
101 }
102
103 Edge* Plan::FindWork() {
104- if (ready_.empty())
105+ if (!more_ready())
106 return NULL;
107
108 Edge* work = ready_.top();
109@@ -595,19 +601,39 @@ void Plan::Dump() const {
110 }
111
112 struct RealCommandRunner : public CommandRunner {
113- explicit RealCommandRunner(const BuildConfig& config) : config_(config) {}
114- virtual ~RealCommandRunner() {}
115- virtual size_t CanRunMore() const;
116+ explicit RealCommandRunner(const BuildConfig& config);
117+ virtual ~RealCommandRunner();
118+ virtual size_t CanRunMore();
119+ virtual bool AcquireToken();
120 virtual bool StartCommand(Edge* edge);
121- virtual bool WaitForCommand(Result* result);
122+ virtual bool WaitForCommand(Result* result, bool more_ready);
123 virtual vector<Edge*> GetActiveEdges();
124 virtual void Abort();
125
126 const BuildConfig& config_;
127+ // copy of config_.max_load_average; can be modified by TokenPool setup
128+ double max_load_average_;
129 SubprocessSet subprocs_;
130+ TokenPool* tokens_;
131 map<const Subprocess*, Edge*> subproc_to_edge_;
132 };
133
134+RealCommandRunner::RealCommandRunner(const BuildConfig& config) : config_(config) {
135+ max_load_average_ = config.max_load_average;
136+ if ((tokens_ = TokenPool::Get()) != NULL) {
137+ if (!tokens_->Setup(config_.parallelism_from_cmdline,
138+ config_.verbosity == BuildConfig::VERBOSE,
139+ max_load_average_)) {
140+ delete tokens_;
141+ tokens_ = NULL;
142+ }
143+ }
144+}
145+
146+RealCommandRunner::~RealCommandRunner() {
147+ delete tokens_;
148+}
149+
150 vector<Edge*> RealCommandRunner::GetActiveEdges() {
151 vector<Edge*> edges;
152 for (map<const Subprocess*, Edge*>::iterator e = subproc_to_edge_.begin();
153@@ -618,9 +644,11 @@ vector<Edge*> RealCommandRunner::GetActi
154
155 void RealCommandRunner::Abort() {
156 subprocs_.Clear();
157+ if (tokens_)
158+ tokens_->Clear();
159 }
160
161-size_t RealCommandRunner::CanRunMore() const {
162+size_t RealCommandRunner::CanRunMore() {
163 size_t subproc_number =
164 subprocs_.running_.size() + subprocs_.finished_.size();
165
166@@ -635,6 +663,13 @@ size_t RealCommandRunner::CanRunMore() c
167 if (capacity < 0)
168 capacity = 0;
169
170+ if (tokens_) {
171+ if (AcquireToken())
172+ return SIZE_MAX;
173+ else
174+ capacity = 0;
175+ }
176+
177 if (capacity == 0 && subprocs_.running_.empty())
178 // Ensure that we make progress.
179 capacity = 1;
180@@ -642,24 +677,42 @@ size_t RealCommandRunner::CanRunMore() c
181 return capacity;
182 }
183
184+bool RealCommandRunner::AcquireToken() {
185+ return (!tokens_ || tokens_->Acquire());
186+}
187+
188 bool RealCommandRunner::StartCommand(Edge* edge) {
189 string command = edge->EvaluateCommand();
190 Subprocess* subproc = subprocs_.Add(command, edge->use_console());
191 if (!subproc)
192 return false;
193+ if (tokens_)
194+ tokens_->Reserve();
195 subproc_to_edge_.insert(make_pair(subproc, edge));
196
197 return true;
198 }
199
200-bool RealCommandRunner::WaitForCommand(Result* result) {
201+bool RealCommandRunner::WaitForCommand(Result* result, bool more_ready) {
202 Subprocess* subproc;
203- while ((subproc = subprocs_.NextFinished()) == NULL) {
204- bool interrupted = subprocs_.DoWork();
205+ subprocs_.ResetTokenAvailable();
206+ while (((subproc = subprocs_.NextFinished()) == NULL) &&
207+ !subprocs_.IsTokenAvailable()) {
208+ bool interrupted = subprocs_.DoWork(more_ready ? tokens_ : NULL);
209 if (interrupted)
210 return false;
211 }
212
213+ // token became available
214+ if (subproc == NULL) {
215+ result->status = ExitTokenAvailable;
216+ return true;
217+ }
218+
219+ // command completed
220+ if (tokens_)
221+ tokens_->Release();
222+
223 result->status = subproc->Finish();
224 result->output = subproc->GetOutput();
225
226@@ -790,7 +843,8 @@ bool Builder::Build(string* err) {
227 // Second, we attempt to wait for / reap the next finished command.
228 while (plan_.more_to_do()) {
229 // See if we can start any more commands.
230- if (failures_allowed) {
231+ bool can_run_more = failures_allowed && plan_.more_ready();
232+ if (can_run_more) {
233 size_t capacity = command_runner_->CanRunMore();
234 while (capacity > 0) {
235 Edge* edge = plan_.FindWork();
236@@ -833,7 +887,7 @@ bool Builder::Build(string* err) {
237 // See if we can reap any finished commands.
238 if (pending_commands) {
239 CommandRunner::Result result;
240- if (!command_runner_->WaitForCommand(&result) ||
241+ if (!command_runner_->WaitForCommand(&result, can_run_more) ||
242 result.status == ExitInterrupted) {
243 Cleanup();
244 status_->BuildFinished();
245@@ -841,6 +895,10 @@ bool Builder::Build(string* err) {
246 return false;
247 }
248
249+ // We might be able to start another command; start the main loop over.
250+ if (result.status == ExitTokenAvailable)
251+ continue;
252+
253 --pending_commands;
254 if (!FinishCommand(&result, err)) {
255 Cleanup();
256--- a/src/build.h
257+++ b/src/build.h
258@@ -51,6 +51,9 @@ struct Plan {
259 /// Returns true if there's more work to be done.
260 bool more_to_do() const { return wanted_edges_ > 0 && command_edges_ > 0; }
261
262+ /// Returns true if there's more edges ready to start
263+ bool more_ready() const { return !ready_.empty(); }
264+
265 /// Dumps the current state of the plan.
266 void Dump() const;
267
268@@ -145,7 +148,8 @@ private:
269 /// RealCommandRunner is an implementation that actually runs commands.
270 struct CommandRunner {
271 virtual ~CommandRunner() {}
272- virtual size_t CanRunMore() const = 0;
273+ virtual size_t CanRunMore() = 0;
274+ virtual bool AcquireToken() = 0;
275 virtual bool StartCommand(Edge* edge) = 0;
276
277 /// The result of waiting for a command.
278@@ -157,7 +161,9 @@ struct CommandRunner {
279 bool success() const { return status == ExitSuccess; }
280 };
281 /// Wait for a command to complete, or return false if interrupted.
282- virtual bool WaitForCommand(Result* result) = 0;
283+ /// If more_ready is true then the optional TokenPool is monitored too
284+ /// and we return when a token becomes available.
285+ virtual bool WaitForCommand(Result* result, bool more_ready) = 0;
286
287 virtual std::vector<Edge*> GetActiveEdges() { return std::vector<Edge*>(); }
288 virtual void Abort() {}
289@@ -165,7 +171,8 @@ struct CommandRunner {
290
291 /// Options (e.g. verbosity, parallelism) passed to a build.
292 struct BuildConfig {
293- BuildConfig() : verbosity(NORMAL), dry_run(false), parallelism(1),
294+ BuildConfig() : verbosity(NORMAL), dry_run(false),
295+ parallelism(1), parallelism_from_cmdline(false),
296 failures_allowed(1), max_load_average(-0.0f) {}
297
298 enum Verbosity {
299@@ -177,6 +184,7 @@ struct BuildConfig {
300 Verbosity verbosity;
301 bool dry_run;
302 int parallelism;
303+ bool parallelism_from_cmdline;
304 int failures_allowed;
305 /// The maximum load average we must not exceed. A negative value
306 /// means that we do not have any limit.
307--- /dev/null
308+++ b/src/tokenpool-gnu-make.cc
309@@ -0,0 +1,108 @@
310+// Copyright 2016-2018 Google Inc. All Rights Reserved.
311+//
312+// Licensed under the Apache License, Version 2.0 (the "License");
313+// you may not use this file except in compliance with the License.
314+// You may obtain a copy of the License at
315+//
316+// http://www.apache.org/licenses/LICENSE-2.0
317+//
318+// Unless required by applicable law or agreed to in writing, software
319+// distributed under the License is distributed on an "AS IS" BASIS,
320+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
321+// See the License for the specific language governing permissions and
322+// limitations under the License.
323+
324+#include "tokenpool-gnu-make.h"
325+
326+#include <stdlib.h>
327+#include <stdio.h>
328+#include <string.h>
329+
330+#include "line_printer.h"
331+
332+// TokenPool implementation for GNU make jobserver - common bits
333+// every instance owns an implicit token -> available_ == 1
334+GNUmakeTokenPool::GNUmakeTokenPool() : available_(1), used_(0) {
335+}
336+
337+GNUmakeTokenPool::~GNUmakeTokenPool() {
338+}
339+
340+bool GNUmakeTokenPool::Setup(bool ignore,
341+ bool verbose,
342+ double& max_load_average) {
343+ const char* value = GetEnv("MAKEFLAGS");
344+ if (!value)
345+ return false;
346+
347+ // GNU make <= 4.1
348+ const char* jobserver = strstr(value, "--jobserver-fds=");
349+ if (!jobserver)
350+ // GNU make => 4.2
351+ jobserver = strstr(value, "--jobserver-auth=");
352+ if (jobserver) {
353+ LinePrinter printer;
354+
355+ if (ignore) {
356+ printer.PrintOnNewLine("ninja: warning: -jN forced on command line; ignoring GNU make jobserver.\n");
357+ } else {
358+ if (ParseAuth(jobserver)) {
359+ const char* l_arg = strstr(value, " -l");
360+ int load_limit = -1;
361+
362+ if (verbose) {
363+ printer.PrintOnNewLine("ninja: using GNU make jobserver.\n");
364+ }
365+
366+ // translate GNU make -lN to ninja -lN
367+ if (l_arg &&
368+ (sscanf(l_arg + 3, "%d ", &load_limit) == 1) &&
369+ (load_limit > 0)) {
370+ max_load_average = load_limit;
371+ }
372+
373+ return true;
374+ }
375+ }
376+ }
377+
378+ return false;
379+}
380+
381+bool GNUmakeTokenPool::Acquire() {
382+ if (available_ > 0)
383+ return true;
384+
385+ if (AcquireToken()) {
386+ // token acquired
387+ available_++;
388+ return true;
389+ }
390+
391+ // no token available
392+ return false;
393+}
394+
395+void GNUmakeTokenPool::Reserve() {
396+ available_--;
397+ used_++;
398+}
399+
400+void GNUmakeTokenPool::Return() {
401+ if (ReturnToken())
402+ available_--;
403+}
404+
405+void GNUmakeTokenPool::Release() {
406+ available_++;
407+ used_--;
408+ if (available_ > 1)
409+ Return();
410+}
411+
412+void GNUmakeTokenPool::Clear() {
413+ while (used_ > 0)
414+ Release();
415+ while (available_ > 1)
416+ Return();
417+}
418--- /dev/null
419+++ b/src/tokenpool.h
420@@ -0,0 +1,42 @@
421+// Copyright 2016-2018 Google Inc. All Rights Reserved.
422+//
423+// Licensed under the Apache License, Version 2.0 (the "License");
424+// you may not use this file except in compliance with the License.
425+// You may obtain a copy of the License at
426+//
427+// http://www.apache.org/licenses/LICENSE-2.0
428+//
429+// Unless required by applicable law or agreed to in writing, software
430+// distributed under the License is distributed on an "AS IS" BASIS,
431+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
432+// See the License for the specific language governing permissions and
433+// limitations under the License.
434+
435+#ifdef _WIN32
436+#include <windows.h>
437+#endif
438+
439+// interface to token pool
440+struct TokenPool {
441+ virtual ~TokenPool() {}
442+
443+ virtual bool Acquire() = 0;
444+ virtual void Reserve() = 0;
445+ virtual void Release() = 0;
446+ virtual void Clear() = 0;
447+
448+ // returns false if token pool setup failed
449+ virtual bool Setup(bool ignore, bool verbose, double& max_load_average) = 0;
450+
451+#ifdef _WIN32
452+ virtual void WaitForTokenAvailability(HANDLE ioport) = 0;
453+ // returns true if a token has become available
454+ // key is result from GetQueuedCompletionStatus()
455+ virtual bool TokenIsAvailable(ULONG_PTR key) = 0;
456+#else
457+ virtual int GetMonitorFd() = 0;
458+#endif
459+
460+ // returns NULL if token pool is not available
461+ static TokenPool* Get();
462+};
463--- a/src/build_test.cc
464+++ b/src/build_test.cc
465@@ -15,6 +15,7 @@
466 #include "build.h"
467
468 #include <assert.h>
469+#include <stdarg.h>
470 #include <climits>
471 #include <stdint.h>
472
473@@ -521,9 +522,10 @@ struct FakeCommandRunner : public Comman
474 max_active_edges_(1), fs_(fs) {}
475
476 // CommandRunner impl
477- virtual size_t CanRunMore() const;
478+ virtual size_t CanRunMore();
479+ virtual bool AcquireToken();
480 virtual bool StartCommand(Edge* edge);
481- virtual bool WaitForCommand(Result* result);
482+ virtual bool WaitForCommand(Result* result, bool more_ready);
483 virtual vector<Edge*> GetActiveEdges();
484 virtual void Abort();
485
486@@ -622,13 +624,17 @@ void BuildTest::RebuildTarget(const stri
487 builder.command_runner_.release();
488 }
489
490-size_t FakeCommandRunner::CanRunMore() const {
491+size_t FakeCommandRunner::CanRunMore() {
492 if (active_edges_.size() < max_active_edges_)
493 return SIZE_MAX;
494
495 return 0;
496 }
497
498+bool FakeCommandRunner::AcquireToken() {
499+ return true;
500+}
501+
502 bool FakeCommandRunner::StartCommand(Edge* edge) {
503 assert(active_edges_.size() < max_active_edges_);
504 assert(find(active_edges_.begin(), active_edges_.end(), edge)
505@@ -720,7 +726,7 @@ bool FakeCommandRunner::StartCommand(Edg
506 return true;
507 }
508
509-bool FakeCommandRunner::WaitForCommand(Result* result) {
510+bool FakeCommandRunner::WaitForCommand(Result* result, bool more_ready) {
511 if (active_edges_.empty())
512 return false;
513
514@@ -4380,3 +4386,355 @@ TEST_F(BuildTest, ValidationWithCircular
515 EXPECT_FALSE(builder_.AddTarget("out", &err));
516 EXPECT_EQ("dependency cycle: validate -> validate_in -> validate", err);
517 }
518+
519+/// The token tests are concerned with the main loop functionality when
520+// the CommandRunner has an active TokenPool. It is therefore intentional
521+// that the plan doesn't complete and that builder_.Build() returns false!
522+
523+/// Fake implementation of CommandRunner that simulates a TokenPool
524+struct FakeTokenCommandRunner : public CommandRunner {
525+ explicit FakeTokenCommandRunner() {}
526+
527+ // CommandRunner impl
528+ virtual bool CanRunMore();
529+ virtual bool AcquireToken();
530+ virtual bool StartCommand(Edge* edge);
531+ virtual bool WaitForCommand(Result* result, bool more_ready);
532+ virtual vector<Edge*> GetActiveEdges();
533+ virtual void Abort();
534+
535+ vector<string> commands_ran_;
536+ vector<Edge *> edges_;
537+
538+ vector<bool> acquire_token_;
539+ vector<bool> can_run_more_;
540+ vector<bool> wait_for_command_;
541+};
542+
543+bool FakeTokenCommandRunner::CanRunMore() {
544+ if (can_run_more_.size() == 0) {
545+ EXPECT_FALSE("unexpected call to CommandRunner::CanRunMore()");
546+ return false;
547+ }
548+
549+ bool result = can_run_more_[0];
550+
551+ can_run_more_.erase(
552+ can_run_more_.begin()
553+ );
554+
555+ return result;
556+}
557+
558+bool FakeTokenCommandRunner::AcquireToken() {
559+ if (acquire_token_.size() == 0) {
560+ EXPECT_FALSE("unexpected call to CommandRunner::AcquireToken()");
561+ return false;
562+ }
563+
564+ bool result = acquire_token_[0];
565+ acquire_token_.erase(acquire_token_.begin());
566+ return result;
567+}
568+
569+bool FakeTokenCommandRunner::StartCommand(Edge* edge) {
570+ commands_ran_.push_back(edge->EvaluateCommand());
571+ edges_.push_back(edge);
572+ return true;
573+}
574+
575+bool FakeTokenCommandRunner::WaitForCommand(Result* result, bool more_ready) {
576+ if (wait_for_command_.size() == 0) {
577+ EXPECT_FALSE("unexpected call to CommandRunner::WaitForCommand()");
578+ return false;
579+ }
580+
581+ bool expected = wait_for_command_[0];
582+ if (expected != more_ready) {
583+ EXPECT_EQ(expected, more_ready);
584+ return false;
585+ }
586+ wait_for_command_.erase(wait_for_command_.begin());
587+
588+ if (edges_.size() == 0)
589+ return false;
590+
591+ Edge* edge = edges_[0];
592+ result->edge = edge;
593+
594+ if (more_ready &&
595+ (edge->rule().name() == "token-available")) {
596+ result->status = ExitTokenAvailable;
597+ } else {
598+ edges_.erase(edges_.begin());
599+ result->status = ExitSuccess;
600+ }
601+
602+ return true;
603+}
604+
605+vector<Edge*> FakeTokenCommandRunner::GetActiveEdges() {
606+ return edges_;
607+}
608+
609+void FakeTokenCommandRunner::Abort() {
610+ edges_.clear();
611+}
612+
613+struct BuildTokenTest : public BuildTest {
614+ virtual void SetUp();
615+ virtual void TearDown();
616+
617+ FakeTokenCommandRunner token_command_runner_;
618+
619+ void ExpectAcquireToken(int count, ...);
620+ void ExpectCanRunMore(int count, ...);
621+ void ExpectWaitForCommand(int count, ...);
622+
623+private:
624+ void EnqueueBooleans(vector<bool>& booleans, int count, va_list ap);
625+};
626+
627+void BuildTokenTest::SetUp() {
628+ BuildTest::SetUp();
629+
630+ // replace FakeCommandRunner with FakeTokenCommandRunner
631+ builder_.command_runner_.release();
632+ builder_.command_runner_.reset(&token_command_runner_);
633+}
634+void BuildTokenTest::TearDown() {
635+ EXPECT_EQ(0u, token_command_runner_.acquire_token_.size());
636+ EXPECT_EQ(0u, token_command_runner_.can_run_more_.size());
637+ EXPECT_EQ(0u, token_command_runner_.wait_for_command_.size());
638+
639+ BuildTest::TearDown();
640+}
641+
642+void BuildTokenTest::ExpectAcquireToken(int count, ...) {
643+ va_list ap;
644+ va_start(ap, count);
645+ EnqueueBooleans(token_command_runner_.acquire_token_, count, ap);
646+ va_end(ap);
647+}
648+
649+void BuildTokenTest::ExpectCanRunMore(int count, ...) {
650+ va_list ap;
651+ va_start(ap, count);
652+ EnqueueBooleans(token_command_runner_.can_run_more_, count, ap);
653+ va_end(ap);
654+}
655+
656+void BuildTokenTest::ExpectWaitForCommand(int count, ...) {
657+ va_list ap;
658+ va_start(ap, count);
659+ EnqueueBooleans(token_command_runner_.wait_for_command_, count, ap);
660+ va_end(ap);
661+}
662+
663+void BuildTokenTest::EnqueueBooleans(vector<bool>& booleans, int count, va_list ap) {
664+ while (count--) {
665+ int value = va_arg(ap, int);
666+ booleans.push_back(!!value); // force bool
667+ }
668+}
669+
670+TEST_F(BuildTokenTest, DoNotAquireToken) {
671+ // plan should execute one command
672+ string err;
673+ EXPECT_TRUE(builder_.AddTarget("cat1", &err));
674+ ASSERT_EQ("", err);
675+
676+ // pretend we can't run anything
677+ ExpectCanRunMore(1, false);
678+
679+ EXPECT_FALSE(builder_.Build(&err));
680+ EXPECT_EQ("stuck [this is a bug]", err);
681+
682+ EXPECT_EQ(0u, token_command_runner_.commands_ran_.size());
683+}
684+
685+TEST_F(BuildTokenTest, DoNotStartWithoutToken) {
686+ // plan should execute one command
687+ string err;
688+ EXPECT_TRUE(builder_.AddTarget("cat1", &err));
689+ ASSERT_EQ("", err);
690+
691+ // we could run a command but do not have a token for it
692+ ExpectCanRunMore(1, true);
693+ ExpectAcquireToken(1, false);
694+
695+ EXPECT_FALSE(builder_.Build(&err));
696+ EXPECT_EQ("stuck [this is a bug]", err);
697+
698+ EXPECT_EQ(0u, token_command_runner_.commands_ran_.size());
699+}
700+
701+TEST_F(BuildTokenTest, CompleteOneStep) {
702+ // plan should execute one command
703+ string err;
704+ EXPECT_TRUE(builder_.AddTarget("cat1", &err));
705+ ASSERT_EQ("", err);
706+
707+ // allow running of one command
708+ ExpectCanRunMore(1, true);
709+ ExpectAcquireToken(1, true);
710+ // block and wait for command to finalize
711+ ExpectWaitForCommand(1, false);
712+
713+ EXPECT_TRUE(builder_.Build(&err));
714+ EXPECT_EQ("", err);
715+
716+ EXPECT_EQ(1u, token_command_runner_.commands_ran_.size());
717+ EXPECT_TRUE(token_command_runner_.commands_ran_[0] == "cat in1 > cat1");
718+}
719+
720+TEST_F(BuildTokenTest, AcquireOneToken) {
721+ // plan should execute more than one command
722+ string err;
723+ EXPECT_TRUE(builder_.AddTarget("cat12", &err));
724+ ASSERT_EQ("", err);
725+
726+ // allow running of one command
727+ ExpectCanRunMore(3, true, false, false);
728+ ExpectAcquireToken(1, true);
729+ // block and wait for command to finalize
730+ ExpectWaitForCommand(1, false);
731+
732+ EXPECT_FALSE(builder_.Build(&err));
733+ EXPECT_EQ("stuck [this is a bug]", err);
734+
735+ EXPECT_EQ(1u, token_command_runner_.commands_ran_.size());
736+ // any of the two dependencies could have been executed
737+ EXPECT_TRUE(token_command_runner_.commands_ran_[0] == "cat in1 > cat1" ||
738+ token_command_runner_.commands_ran_[0] == "cat in1 in2 > cat2");
739+}
740+
741+TEST_F(BuildTokenTest, WantTwoTokens) {
742+ // plan should execute more than one command
743+ string err;
744+ EXPECT_TRUE(builder_.AddTarget("cat12", &err));
745+ ASSERT_EQ("", err);
746+
747+ // allow running of one command
748+ ExpectCanRunMore(3, true, true, false);
749+ ExpectAcquireToken(2, true, false);
750+ // wait for command to finalize or token to become available
751+ ExpectWaitForCommand(1, true);
752+
753+ EXPECT_FALSE(builder_.Build(&err));
754+ EXPECT_EQ("stuck [this is a bug]", err);
755+
756+ EXPECT_EQ(1u, token_command_runner_.commands_ran_.size());
757+ // any of the two dependencies could have been executed
758+ EXPECT_TRUE(token_command_runner_.commands_ran_[0] == "cat in1 > cat1" ||
759+ token_command_runner_.commands_ran_[0] == "cat in1 in2 > cat2");
760+}
761+
762+TEST_F(BuildTokenTest, CompleteTwoSteps) {
763+ ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
764+"build out1: cat in1\n"
765+"build out2: cat out1\n"));
766+
767+ // plan should execute more than one command
768+ string err;
769+ EXPECT_TRUE(builder_.AddTarget("out2", &err));
770+ ASSERT_EQ("", err);
771+
772+ // allow running of two commands
773+ ExpectCanRunMore(2, true, true);
774+ ExpectAcquireToken(2, true, true);
775+ // wait for commands to finalize
776+ ExpectWaitForCommand(2, false, false);
777+
778+ EXPECT_TRUE(builder_.Build(&err));
779+ EXPECT_EQ("", err);
780+
781+ EXPECT_EQ(2u, token_command_runner_.commands_ran_.size());
782+ EXPECT_TRUE(token_command_runner_.commands_ran_[0] == "cat in1 > out1");
783+ EXPECT_TRUE(token_command_runner_.commands_ran_[1] == "cat out1 > out2");
784+}
785+
786+TEST_F(BuildTokenTest, TwoCommandsInParallel) {
787+ ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
788+"rule token-available\n"
789+" command = cat $in > $out\n"
790+"build out1: token-available in1\n"
791+"build out2: token-available in2\n"
792+"build out12: cat out1 out2\n"));
793+
794+ // plan should execute more than one command
795+ string err;
796+ EXPECT_TRUE(builder_.AddTarget("out12", &err));
797+ ASSERT_EQ("", err);
798+
799+ // 1st command: token available -> allow running
800+ // 2nd command: no token available but becomes available later
801+ ExpectCanRunMore(4, true, true, true, false);
802+ ExpectAcquireToken(3, true, false, true);
803+ // 1st call waits for command to finalize or token to become available
804+ // 2nd call waits for command to finalize
805+ // 3rd call waits for command to finalize
806+ ExpectWaitForCommand(3, true, false, false);
807+
808+ EXPECT_FALSE(builder_.Build(&err));
809+ EXPECT_EQ("stuck [this is a bug]", err);
810+
811+ EXPECT_EQ(2u, token_command_runner_.commands_ran_.size());
812+ EXPECT_TRUE((token_command_runner_.commands_ran_[0] == "cat in1 > out1" &&
813+ token_command_runner_.commands_ran_[1] == "cat in2 > out2") ||
814+ (token_command_runner_.commands_ran_[0] == "cat in2 > out2" &&
815+ token_command_runner_.commands_ran_[1] == "cat in1 > out1"));
816+}
817+
818+TEST_F(BuildTokenTest, CompleteThreeStepsSerial) {
819+ // plan should execute more than one command
820+ string err;
821+ EXPECT_TRUE(builder_.AddTarget("cat12", &err));
822+ ASSERT_EQ("", err);
823+
824+ // allow running of all commands
825+ ExpectCanRunMore(4, true, true, true, true);
826+ ExpectAcquireToken(4, true, false, true, true);
827+ // wait for commands to finalize
828+ ExpectWaitForCommand(3, true, false, false);
829+
830+ EXPECT_TRUE(builder_.Build(&err));
831+ EXPECT_EQ("", err);
832+
833+ EXPECT_EQ(3u, token_command_runner_.commands_ran_.size());
834+ EXPECT_TRUE((token_command_runner_.commands_ran_[0] == "cat in1 > cat1" &&
835+ token_command_runner_.commands_ran_[1] == "cat in1 in2 > cat2") ||
836+ (token_command_runner_.commands_ran_[0] == "cat in1 in2 > cat2" &&
837+ token_command_runner_.commands_ran_[1] == "cat in1 > cat1" ));
838+ EXPECT_TRUE(token_command_runner_.commands_ran_[2] == "cat cat1 cat2 > cat12");
839+}
840+
841+TEST_F(BuildTokenTest, CompleteThreeStepsParallel) {
842+ ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
843+"rule token-available\n"
844+" command = cat $in > $out\n"
845+"build out1: token-available in1\n"
846+"build out2: token-available in2\n"
847+"build out12: cat out1 out2\n"));
848+
849+ // plan should execute more than one command
850+ string err;
851+ EXPECT_TRUE(builder_.AddTarget("out12", &err));
852+ ASSERT_EQ("", err);
853+
854+ // allow running of all commands
855+ ExpectCanRunMore(4, true, true, true, true);
856+ ExpectAcquireToken(4, true, false, true, true);
857+ // wait for commands to finalize
858+ ExpectWaitForCommand(4, true, false, false, false);
859+
860+ EXPECT_TRUE(builder_.Build(&err));
861+ EXPECT_EQ("", err);
862+
863+ EXPECT_EQ(3u, token_command_runner_.commands_ran_.size());
864+ EXPECT_TRUE((token_command_runner_.commands_ran_[0] == "cat in1 > out1" &&
865+ token_command_runner_.commands_ran_[1] == "cat in2 > out2") ||
866+ (token_command_runner_.commands_ran_[0] == "cat in2 > out2" &&
867+ token_command_runner_.commands_ran_[1] == "cat in1 > out1"));
868+ EXPECT_TRUE(token_command_runner_.commands_ran_[2] == "cat out1 out2 > out12");
869+}
870--- a/src/exit_status.h
871+++ b/src/exit_status.h
872@@ -18,7 +18,8 @@
873 enum ExitStatus {
874 ExitSuccess,
875 ExitFailure,
876- ExitInterrupted
877+ ExitTokenAvailable,
878+ ExitInterrupted,
879 };
880
881 #endif // NINJA_EXIT_STATUS_H_
882--- a/src/subprocess-posix.cc
883+++ b/src/subprocess-posix.cc
884@@ -13,6 +13,7 @@
885 // limitations under the License.
886
887 #include "subprocess.h"
888+#include "tokenpool.h"
889
890 #include <sys/select.h>
891 #include <assert.h>
892@@ -249,7 +250,7 @@ Subprocess *SubprocessSet::Add(const str
893 }
894
895 #ifdef USE_PPOLL
896-bool SubprocessSet::DoWork() {
897+bool SubprocessSet::DoWork(TokenPool* tokens) {
898 vector<pollfd> fds;
899 nfds_t nfds = 0;
900
901@@ -263,6 +264,12 @@ bool SubprocessSet::DoWork() {
902 ++nfds;
903 }
904
905+ if (tokens) {
906+ pollfd pfd = { tokens->GetMonitorFd(), POLLIN | POLLPRI, 0 };
907+ fds.push_back(pfd);
908+ ++nfds;
909+ }
910+
911 interrupted_ = 0;
912 int ret = ppoll(&fds.front(), nfds, NULL, &old_mask_);
913 if (ret == -1) {
914@@ -295,11 +302,20 @@ bool SubprocessSet::DoWork() {
915 ++i;
916 }
917
918+ if (tokens) {
919+ pollfd *pfd = &fds[nfds - 1];
920+ if (pfd->fd >= 0) {
921+ assert(pfd->fd == tokens->GetMonitorFd());
922+ if (pfd->revents != 0)
923+ token_available_ = true;
924+ }
925+ }
926+
927 return IsInterrupted();
928 }
929
930 #else // !defined(USE_PPOLL)
931-bool SubprocessSet::DoWork() {
932+bool SubprocessSet::DoWork(TokenPool* tokens) {
933 fd_set set;
934 int nfds = 0;
935 FD_ZERO(&set);
936@@ -314,6 +330,13 @@ bool SubprocessSet::DoWork() {
937 }
938 }
939
940+ if (tokens) {
941+ int fd = tokens->GetMonitorFd();
942+ FD_SET(fd, &set);
943+ if (nfds < fd+1)
944+ nfds = fd+1;
945+ }
946+
947 interrupted_ = 0;
948 int ret = pselect(nfds, &set, 0, 0, 0, &old_mask_);
949 if (ret == -1) {
950@@ -342,6 +365,12 @@ bool SubprocessSet::DoWork() {
951 ++i;
952 }
953
954+ if (tokens) {
955+ int fd = tokens->GetMonitorFd();
956+ if ((fd >= 0) && FD_ISSET(fd, &set))
957+ token_available_ = true;
958+ }
959+
960 return IsInterrupted();
961 }
962 #endif // !defined(USE_PPOLL)
963--- a/src/subprocess-win32.cc
964+++ b/src/subprocess-win32.cc
965@@ -13,6 +13,7 @@
966 // limitations under the License.
967
968 #include "subprocess.h"
969+#include "tokenpool.h"
970
971 #include <assert.h>
972 #include <stdio.h>
973@@ -251,11 +252,14 @@ Subprocess *SubprocessSet::Add(const str
974 return subprocess;
975 }
976
977-bool SubprocessSet::DoWork() {
978+bool SubprocessSet::DoWork(TokenPool* tokens) {
979 DWORD bytes_read;
980 Subprocess* subproc;
981 OVERLAPPED* overlapped;
982
983+ if (tokens)
984+ tokens->WaitForTokenAvailability(ioport_);
985+
986 if (!GetQueuedCompletionStatus(ioport_, &bytes_read, (PULONG_PTR)&subproc,
987 &overlapped, INFINITE)) {
988 if (GetLastError() != ERROR_BROKEN_PIPE)
989@@ -266,6 +270,11 @@ bool SubprocessSet::DoWork() {
990 // delivered by NotifyInterrupted above.
991 return true;
992
993+ if (tokens && tokens->TokenIsAvailable((ULONG_PTR)subproc)) {
994+ token_available_ = true;
995+ return false;
996+ }
997+
998 subproc->OnPipeReady();
999
1000 if (subproc->Done()) {
1001--- a/src/subprocess.h
1002+++ b/src/subprocess.h
1003@@ -76,6 +76,8 @@ struct Subprocess {
1004 friend struct SubprocessSet;
1005 };
1006
1007+struct TokenPool;
1008+
1009 /// SubprocessSet runs a ppoll/pselect() loop around a set of Subprocesses.
1010 /// DoWork() waits for any state change in subprocesses; finished_
1011 /// is a queue of subprocesses as they finish.
1012@@ -84,13 +86,17 @@ struct SubprocessSet {
1013 ~SubprocessSet();
1014
1015 Subprocess* Add(const std::string& command, bool use_console = false);
1016- bool DoWork();
1017+ bool DoWork(TokenPool* tokens);
1018 Subprocess* NextFinished();
1019 void Clear();
1020
1021 std::vector<Subprocess*> running_;
1022 std::queue<Subprocess*> finished_;
1023
1024+ bool token_available_;
1025+ bool IsTokenAvailable() { return token_available_; }
1026+ void ResetTokenAvailable() { token_available_ = false; }
1027+
1028 #ifdef _WIN32
1029 static BOOL WINAPI NotifyInterrupted(DWORD dwCtrlType);
1030 static HANDLE ioport_;
1031--- a/src/subprocess_test.cc
1032+++ b/src/subprocess_test.cc
1033@@ -13,6 +13,7 @@
1034 // limitations under the License.
1035
1036 #include "subprocess.h"
1037+#include "tokenpool.h"
1038
1039 #include "test.h"
1040
1041@@ -34,8 +35,30 @@ const char* kSimpleCommand = "cmd /c dir
1042 const char* kSimpleCommand = "ls /";
1043 #endif
1044
1045+struct TestTokenPool : public TokenPool {
1046+ bool Acquire() { return false; }
1047+ void Reserve() {}
1048+ void Release() {}
1049+ void Clear() {}
1050+ bool Setup(bool ignore_unused, bool verbose, double& max_load_average) { return false; }
1051+
1052+#ifdef _WIN32
1053+ bool _token_available;
1054+ void WaitForTokenAvailability(HANDLE ioport) {
1055+ if (_token_available)
1056+ // unblock GetQueuedCompletionStatus()
1057+ PostQueuedCompletionStatus(ioport, 0, (ULONG_PTR) this, NULL);
1058+ }
1059+ bool TokenIsAvailable(ULONG_PTR key) { return key == (ULONG_PTR) this; }
1060+#else
1061+ int _fd;
1062+ int GetMonitorFd() { return _fd; }
1063+#endif
1064+};
1065+
1066 struct SubprocessTest : public testing::Test {
1067 SubprocessSet subprocs_;
1068+ TestTokenPool tokens_;
1069 };
1070
1071 } // anonymous namespace
1072@@ -45,10 +68,12 @@ TEST_F(SubprocessTest, BadCommandStderr)
1073 Subprocess* subproc = subprocs_.Add("cmd /c ninja_no_such_command");
1074 ASSERT_NE((Subprocess *) 0, subproc);
1075
1076+ subprocs_.ResetTokenAvailable();
1077 while (!subproc->Done()) {
1078 // Pretend we discovered that stderr was ready for writing.
1079- subprocs_.DoWork();
1080+ subprocs_.DoWork(NULL);
1081 }
1082+ ASSERT_FALSE(subprocs_.IsTokenAvailable());
1083
1084 EXPECT_EQ(ExitFailure, subproc->Finish());
1085 EXPECT_NE("", subproc->GetOutput());
1086@@ -59,10 +84,12 @@ TEST_F(SubprocessTest, NoSuchCommand) {
1087 Subprocess* subproc = subprocs_.Add("ninja_no_such_command");
1088 ASSERT_NE((Subprocess *) 0, subproc);
1089
1090+ subprocs_.ResetTokenAvailable();
1091 while (!subproc->Done()) {
1092 // Pretend we discovered that stderr was ready for writing.
1093- subprocs_.DoWork();
1094+ subprocs_.DoWork(NULL);
1095 }
1096+ ASSERT_FALSE(subprocs_.IsTokenAvailable());
1097
1098 EXPECT_EQ(ExitFailure, subproc->Finish());
1099 EXPECT_NE("", subproc->GetOutput());
1100@@ -78,9 +105,11 @@ TEST_F(SubprocessTest, InterruptChild) {
1101 Subprocess* subproc = subprocs_.Add("kill -INT $$");
1102 ASSERT_NE((Subprocess *) 0, subproc);
1103
1104+ subprocs_.ResetTokenAvailable();
1105 while (!subproc->Done()) {
1106- subprocs_.DoWork();
1107+ subprocs_.DoWork(NULL);
1108 }
1109+ ASSERT_FALSE(subprocs_.IsTokenAvailable());
1110
1111 EXPECT_EQ(ExitInterrupted, subproc->Finish());
1112 }
1113@@ -90,7 +119,7 @@ TEST_F(SubprocessTest, InterruptParent)
1114 ASSERT_NE((Subprocess *) 0, subproc);
1115
1116 while (!subproc->Done()) {
1117- bool interrupted = subprocs_.DoWork();
1118+ bool interrupted = subprocs_.DoWork(NULL);
1119 if (interrupted)
1120 return;
1121 }
1122@@ -102,9 +131,11 @@ TEST_F(SubprocessTest, InterruptChildWit
1123 Subprocess* subproc = subprocs_.Add("kill -TERM $$");
1124 ASSERT_NE((Subprocess *) 0, subproc);
1125
1126+ subprocs_.ResetTokenAvailable();
1127 while (!subproc->Done()) {
1128- subprocs_.DoWork();
1129+ subprocs_.DoWork(NULL);
1130 }
1131+ ASSERT_FALSE(subprocs_.IsTokenAvailable());
1132
1133 EXPECT_EQ(ExitInterrupted, subproc->Finish());
1134 }
1135@@ -114,7 +145,7 @@ TEST_F(SubprocessTest, InterruptParentWi
1136 ASSERT_NE((Subprocess *) 0, subproc);
1137
1138 while (!subproc->Done()) {
1139- bool interrupted = subprocs_.DoWork();
1140+ bool interrupted = subprocs_.DoWork(NULL);
1141 if (interrupted)
1142 return;
1143 }
1144@@ -126,9 +157,11 @@ TEST_F(SubprocessTest, InterruptChildWit
1145 Subprocess* subproc = subprocs_.Add("kill -HUP $$");
1146 ASSERT_NE((Subprocess *) 0, subproc);
1147
1148+ subprocs_.ResetTokenAvailable();
1149 while (!subproc->Done()) {
1150- subprocs_.DoWork();
1151+ subprocs_.DoWork(NULL);
1152 }
1153+ ASSERT_FALSE(subprocs_.IsTokenAvailable());
1154
1155 EXPECT_EQ(ExitInterrupted, subproc->Finish());
1156 }
1157@@ -138,7 +171,7 @@ TEST_F(SubprocessTest, InterruptParentWi
1158 ASSERT_NE((Subprocess *) 0, subproc);
1159
1160 while (!subproc->Done()) {
1161- bool interrupted = subprocs_.DoWork();
1162+ bool interrupted = subprocs_.DoWork(NULL);
1163 if (interrupted)
1164 return;
1165 }
1166@@ -153,9 +186,11 @@ TEST_F(SubprocessTest, Console) {
1167 subprocs_.Add("test -t 0 -a -t 1 -a -t 2", /*use_console=*/true);
1168 ASSERT_NE((Subprocess*)0, subproc);
1169
1170+ subprocs_.ResetTokenAvailable();
1171 while (!subproc->Done()) {
1172- subprocs_.DoWork();
1173+ subprocs_.DoWork(NULL);
1174 }
1175+ ASSERT_FALSE(subprocs_.IsTokenAvailable());
1176
1177 EXPECT_EQ(ExitSuccess, subproc->Finish());
1178 }
1179@@ -167,9 +202,11 @@ TEST_F(SubprocessTest, SetWithSingle) {
1180 Subprocess* subproc = subprocs_.Add(kSimpleCommand);
1181 ASSERT_NE((Subprocess *) 0, subproc);
1182
1183+ subprocs_.ResetTokenAvailable();
1184 while (!subproc->Done()) {
1185- subprocs_.DoWork();
1186+ subprocs_.DoWork(NULL);
1187 }
1188+ ASSERT_FALSE(subprocs_.IsTokenAvailable());
1189 ASSERT_EQ(ExitSuccess, subproc->Finish());
1190 ASSERT_NE("", subproc->GetOutput());
1191
1192@@ -200,12 +237,13 @@ TEST_F(SubprocessTest, SetWithMulti) {
1193 ASSERT_EQ("", processes[i]->GetOutput());
1194 }
1195
1196+ subprocs_.ResetTokenAvailable();
1197 while (!processes[0]->Done() || !processes[1]->Done() ||
1198 !processes[2]->Done()) {
1199 ASSERT_GT(subprocs_.running_.size(), 0u);
1200- subprocs_.DoWork();
1201+ subprocs_.DoWork(NULL);
1202 }
1203-
1204+ ASSERT_FALSE(subprocs_.IsTokenAvailable());
1205 ASSERT_EQ(0u, subprocs_.running_.size());
1206 ASSERT_EQ(3u, subprocs_.finished_.size());
1207
1208@@ -237,8 +275,10 @@ TEST_F(SubprocessTest, SetWithLots) {
1209 ASSERT_NE((Subprocess *) 0, subproc);
1210 procs.push_back(subproc);
1211 }
1212+ subprocs_.ResetTokenAvailable();
1213 while (!subprocs_.running_.empty())
1214- subprocs_.DoWork();
1215+ subprocs_.DoWork(NULL);
1216+ ASSERT_FALSE(subprocs_.IsTokenAvailable());
1217 for (size_t i = 0; i < procs.size(); ++i) {
1218 ASSERT_EQ(ExitSuccess, procs[i]->Finish());
1219 ASSERT_NE("", procs[i]->GetOutput());
1220@@ -254,10 +294,91 @@ TEST_F(SubprocessTest, SetWithLots) {
1221 // that stdin is closed.
1222 TEST_F(SubprocessTest, ReadStdin) {
1223 Subprocess* subproc = subprocs_.Add("cat -");
1224+ subprocs_.ResetTokenAvailable();
1225 while (!subproc->Done()) {
1226- subprocs_.DoWork();
1227+ subprocs_.DoWork(NULL);
1228 }
1229+ ASSERT_FALSE(subprocs_.IsTokenAvailable());
1230 ASSERT_EQ(ExitSuccess, subproc->Finish());
1231 ASSERT_EQ(1u, subprocs_.finished_.size());
1232 }
1233 #endif // _WIN32
1234+
1235+TEST_F(SubprocessTest, TokenAvailable) {
1236+ Subprocess* subproc = subprocs_.Add(kSimpleCommand);
1237+ ASSERT_NE((Subprocess *) 0, subproc);
1238+
1239+ // simulate GNUmake jobserver pipe with 1 token
1240+#ifdef _WIN32
1241+ tokens_._token_available = true;
1242+#else
1243+ int fds[2];
1244+ ASSERT_EQ(0u, pipe(fds));
1245+ tokens_._fd = fds[0];
1246+ ASSERT_EQ(1u, write(fds[1], "T", 1));
1247+#endif
1248+
1249+ subprocs_.ResetTokenAvailable();
1250+ subprocs_.DoWork(&tokens_);
1251+#ifdef _WIN32
1252+ tokens_._token_available = false;
1253+ // we need to loop here as we have no control where the token
1254+ // I/O completion post ends up in the queue
1255+ while (!subproc->Done() && !subprocs_.IsTokenAvailable()) {
1256+ subprocs_.DoWork(&tokens_);
1257+ }
1258+#endif
1259+
1260+ EXPECT_TRUE(subprocs_.IsTokenAvailable());
1261+ EXPECT_EQ(0u, subprocs_.finished_.size());
1262+
1263+ // remove token to let DoWork() wait for command again
1264+#ifndef _WIN32
1265+ char token;
1266+ ASSERT_EQ(1u, read(fds[0], &token, 1));
1267+#endif
1268+
1269+ while (!subproc->Done()) {
1270+ subprocs_.DoWork(&tokens_);
1271+ }
1272+
1273+#ifndef _WIN32
1274+ close(fds[1]);
1275+ close(fds[0]);
1276+#endif
1277+
1278+ EXPECT_EQ(ExitSuccess, subproc->Finish());
1279+ EXPECT_NE("", subproc->GetOutput());
1280+
1281+ EXPECT_EQ(1u, subprocs_.finished_.size());
1282+}
1283+
1284+TEST_F(SubprocessTest, TokenNotAvailable) {
1285+ Subprocess* subproc = subprocs_.Add(kSimpleCommand);
1286+ ASSERT_NE((Subprocess *) 0, subproc);
1287+
1288+ // simulate GNUmake jobserver pipe with 0 tokens
1289+#ifdef _WIN32
1290+ tokens_._token_available = false;
1291+#else
1292+ int fds[2];
1293+ ASSERT_EQ(0u, pipe(fds));
1294+ tokens_._fd = fds[0];
1295+#endif
1296+
1297+ subprocs_.ResetTokenAvailable();
1298+ while (!subproc->Done()) {
1299+ subprocs_.DoWork(&tokens_);
1300+ }
1301+
1302+#ifndef _WIN32
1303+ close(fds[1]);
1304+ close(fds[0]);
1305+#endif
1306+
1307+ EXPECT_FALSE(subprocs_.IsTokenAvailable());
1308+ EXPECT_EQ(ExitSuccess, subproc->Finish());
1309+ EXPECT_NE("", subproc->GetOutput());
1310+
1311+ EXPECT_EQ(1u, subprocs_.finished_.size());
1312+}
1313--- a/src/ninja.cc
1314+++ b/src/ninja.cc
1315@@ -1466,6 +1466,7 @@ int ReadFlags(int* argc, char*** argv,
1316 // We want to run N jobs in parallel. For N = 0, INT_MAX
1317 // is close enough to infinite for most sane builds.
1318 config->parallelism = value > 0 ? value : INT_MAX;
1319+ config->parallelism_from_cmdline = true;
1320 deferGuessParallelism.needGuess = false;
1321 break;
1322 }
1323--- /dev/null
1324+++ b/src/tokenpool_test.cc
1325@@ -0,0 +1,279 @@
1326+// Copyright 2018 Google Inc. All Rights Reserved.
1327+//
1328+// Licensed under the Apache License, Version 2.0 (the "License");
1329+// you may not use this file except in compliance with the License.
1330+// You may obtain a copy of the License at
1331+//
1332+// http://www.apache.org/licenses/LICENSE-2.0
1333+//
1334+// Unless required by applicable law or agreed to in writing, software
1335+// distributed under the License is distributed on an "AS IS" BASIS,
1336+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1337+// See the License for the specific language governing permissions and
1338+// limitations under the License.
1339+
1340+#include "tokenpool.h"
1341+
1342+#include "test.h"
1343+
1344+#ifdef _WIN32
1345+#include <windows.h>
1346+#else
1347+#include <unistd.h>
1348+#endif
1349+
1350+#include <stdio.h>
1351+#include <stdlib.h>
1352+
1353+#ifdef _WIN32
1354+// should contain all valid characters
1355+#define SEMAPHORE_NAME "abcdefghijklmnopqrstwxyz01234567890_"
1356+#define AUTH_FORMAT(tmpl) "foo " tmpl "=%s bar"
1357+#define ENVIRONMENT_CLEAR() SetEnvironmentVariable("MAKEFLAGS", NULL)
1358+#define ENVIRONMENT_INIT(v) SetEnvironmentVariable("MAKEFLAGS", v)
1359+#else
1360+#define AUTH_FORMAT(tmpl) "foo " tmpl "=%d,%d bar"
1361+#define ENVIRONMENT_CLEAR() unsetenv("MAKEFLAGS")
1362+#define ENVIRONMENT_INIT(v) setenv("MAKEFLAGS", v, true)
1363+#endif
1364+
1365+namespace {
1366+
1367+const double kLoadAverageDefault = -1.23456789;
1368+
1369+struct TokenPoolTest : public testing::Test {
1370+ double load_avg_;
1371+ TokenPool* tokens_;
1372+ char buf_[1024];
1373+#ifdef _WIN32
1374+ const char* semaphore_name_;
1375+ HANDLE semaphore_;
1376+#else
1377+ int fds_[2];
1378+
1379+ char random() {
1380+ return int((rand() / double(RAND_MAX)) * 256);
1381+ }
1382+#endif
1383+
1384+ virtual void SetUp() {
1385+ load_avg_ = kLoadAverageDefault;
1386+ tokens_ = NULL;
1387+ ENVIRONMENT_CLEAR();
1388+#ifdef _WIN32
1389+ semaphore_name_ = SEMAPHORE_NAME;
1390+ if ((semaphore_ = CreateSemaphore(0, 0, 2, SEMAPHORE_NAME)) == NULL)
1391+#else
1392+ if (pipe(fds_) < 0)
1393+#endif
1394+ ASSERT_TRUE(false);
1395+ }
1396+
1397+ void CreatePool(const char* format, bool ignore_jobserver = false) {
1398+ if (format) {
1399+ sprintf(buf_, format,
1400+#ifdef _WIN32
1401+ semaphore_name_
1402+#else
1403+ fds_[0], fds_[1]
1404+#endif
1405+ );
1406+ ENVIRONMENT_INIT(buf_);
1407+ }
1408+ if ((tokens_ = TokenPool::Get()) != NULL) {
1409+ if (!tokens_->Setup(ignore_jobserver, false, load_avg_)) {
1410+ delete tokens_;
1411+ tokens_ = NULL;
1412+ }
1413+ }
1414+ }
1415+
1416+ void CreateDefaultPool() {
1417+ CreatePool(AUTH_FORMAT("--jobserver-auth"));
1418+ }
1419+
1420+ virtual void TearDown() {
1421+ if (tokens_)
1422+ delete tokens_;
1423+#ifdef _WIN32
1424+ CloseHandle(semaphore_);
1425+#else
1426+ close(fds_[0]);
1427+ close(fds_[1]);
1428+#endif
1429+ ENVIRONMENT_CLEAR();
1430+ }
1431+};
1432+
1433+} // anonymous namespace
1434+
1435+// verifies none implementation
1436+TEST_F(TokenPoolTest, NoTokenPool) {
1437+ CreatePool(NULL, false);
1438+
1439+ EXPECT_EQ(NULL, tokens_);
1440+ EXPECT_EQ(kLoadAverageDefault, load_avg_);
1441+}
1442+
1443+TEST_F(TokenPoolTest, SuccessfulOldSetup) {
1444+ // GNUmake <= 4.1
1445+ CreatePool(AUTH_FORMAT("--jobserver-fds"));
1446+
1447+ EXPECT_NE(NULL, tokens_);
1448+ EXPECT_EQ(kLoadAverageDefault, load_avg_);
1449+}
1450+
1451+TEST_F(TokenPoolTest, SuccessfulNewSetup) {
1452+ // GNUmake => 4.2
1453+ CreateDefaultPool();
1454+
1455+ EXPECT_NE(NULL, tokens_);
1456+ EXPECT_EQ(kLoadAverageDefault, load_avg_);
1457+}
1458+
1459+TEST_F(TokenPoolTest, IgnoreWithJN) {
1460+ CreatePool(AUTH_FORMAT("--jobserver-auth"), true);
1461+
1462+ EXPECT_EQ(NULL, tokens_);
1463+ EXPECT_EQ(kLoadAverageDefault, load_avg_);
1464+}
1465+
1466+TEST_F(TokenPoolTest, HonorLN) {
1467+ CreatePool(AUTH_FORMAT("-l9 --jobserver-auth"));
1468+
1469+ EXPECT_NE(NULL, tokens_);
1470+ EXPECT_EQ(9.0, load_avg_);
1471+}
1472+
1473+#ifdef _WIN32
1474+TEST_F(TokenPoolTest, SemaphoreNotFound) {
1475+ semaphore_name_ = SEMAPHORE_NAME "_foobar";
1476+ CreateDefaultPool();
1477+
1478+ EXPECT_EQ(NULL, tokens_);
1479+ EXPECT_EQ(kLoadAverageDefault, load_avg_);
1480+}
1481+
1482+TEST_F(TokenPoolTest, TokenIsAvailable) {
1483+ CreateDefaultPool();
1484+
1485+ ASSERT_NE(NULL, tokens_);
1486+ EXPECT_EQ(kLoadAverageDefault, load_avg_);
1487+
1488+ EXPECT_TRUE(tokens_->TokenIsAvailable((ULONG_PTR)tokens_));
1489+}
1490+#else
1491+TEST_F(TokenPoolTest, MonitorFD) {
1492+ CreateDefaultPool();
1493+
1494+ ASSERT_NE(NULL, tokens_);
1495+ EXPECT_EQ(kLoadAverageDefault, load_avg_);
1496+
1497+ EXPECT_EQ(fds_[0], tokens_->GetMonitorFd());
1498+}
1499+#endif
1500+
1501+TEST_F(TokenPoolTest, ImplicitToken) {
1502+ CreateDefaultPool();
1503+
1504+ ASSERT_NE(NULL, tokens_);
1505+ EXPECT_EQ(kLoadAverageDefault, load_avg_);
1506+
1507+ EXPECT_TRUE(tokens_->Acquire());
1508+ tokens_->Reserve();
1509+ EXPECT_FALSE(tokens_->Acquire());
1510+ tokens_->Release();
1511+ EXPECT_TRUE(tokens_->Acquire());
1512+}
1513+
1514+TEST_F(TokenPoolTest, TwoTokens) {
1515+ CreateDefaultPool();
1516+
1517+ ASSERT_NE(NULL, tokens_);
1518+ EXPECT_EQ(kLoadAverageDefault, load_avg_);
1519+
1520+ // implicit token
1521+ EXPECT_TRUE(tokens_->Acquire());
1522+ tokens_->Reserve();
1523+ EXPECT_FALSE(tokens_->Acquire());
1524+
1525+ // jobserver offers 2nd token
1526+#ifdef _WIN32
1527+ LONG previous;
1528+ ASSERT_TRUE(ReleaseSemaphore(semaphore_, 1, &previous));
1529+ ASSERT_EQ(0, previous);
1530+#else
1531+ char test_tokens[1] = { random() };
1532+ ASSERT_EQ(1u, write(fds_[1], test_tokens, sizeof(test_tokens)));
1533+#endif
1534+ EXPECT_TRUE(tokens_->Acquire());
1535+ tokens_->Reserve();
1536+ EXPECT_FALSE(tokens_->Acquire());
1537+
1538+ // release 2nd token
1539+ tokens_->Release();
1540+ EXPECT_TRUE(tokens_->Acquire());
1541+
1542+ // release implicit token - must return 2nd token back to jobserver
1543+ tokens_->Release();
1544+ EXPECT_TRUE(tokens_->Acquire());
1545+
1546+ // there must be one token available
1547+#ifdef _WIN32
1548+ EXPECT_EQ(WAIT_OBJECT_0, WaitForSingleObject(semaphore_, 0));
1549+ EXPECT_TRUE(ReleaseSemaphore(semaphore_, 1, &previous));
1550+ EXPECT_EQ(0, previous);
1551+#else
1552+ EXPECT_EQ(1u, read(fds_[0], buf_, sizeof(buf_)));
1553+ EXPECT_EQ(test_tokens[0], buf_[0]);
1554+#endif
1555+
1556+ // implicit token
1557+ EXPECT_TRUE(tokens_->Acquire());
1558+}
1559+
1560+TEST_F(TokenPoolTest, Clear) {
1561+ CreateDefaultPool();
1562+
1563+ ASSERT_NE(NULL, tokens_);
1564+ EXPECT_EQ(kLoadAverageDefault, load_avg_);
1565+
1566+ // implicit token
1567+ EXPECT_TRUE(tokens_->Acquire());
1568+ tokens_->Reserve();
1569+ EXPECT_FALSE(tokens_->Acquire());
1570+
1571+ // jobserver offers 2nd & 3rd token
1572+#ifdef _WIN32
1573+ LONG previous;
1574+ ASSERT_TRUE(ReleaseSemaphore(semaphore_, 2, &previous));
1575+ ASSERT_EQ(0, previous);
1576+#else
1577+ char test_tokens[2] = { random(), random() };
1578+ ASSERT_EQ(2u, write(fds_[1], test_tokens, sizeof(test_tokens)));
1579+#endif
1580+ EXPECT_TRUE(tokens_->Acquire());
1581+ tokens_->Reserve();
1582+ EXPECT_TRUE(tokens_->Acquire());
1583+ tokens_->Reserve();
1584+ EXPECT_FALSE(tokens_->Acquire());
1585+
1586+ tokens_->Clear();
1587+ EXPECT_TRUE(tokens_->Acquire());
1588+
1589+ // there must be two tokens available
1590+#ifdef _WIN32
1591+ EXPECT_EQ(WAIT_OBJECT_0, WaitForSingleObject(semaphore_, 0));
1592+ EXPECT_EQ(WAIT_OBJECT_0, WaitForSingleObject(semaphore_, 0));
1593+ EXPECT_TRUE(ReleaseSemaphore(semaphore_, 2, &previous));
1594+ EXPECT_EQ(0, previous);
1595+#else
1596+ EXPECT_EQ(2u, read(fds_[0], buf_, sizeof(buf_)));
1597+ // tokens are pushed onto a stack, hence returned in reverse order
1598+ EXPECT_EQ(test_tokens[0], buf_[1]);
1599+ EXPECT_EQ(test_tokens[1], buf_[0]);
1600+#endif
1601+
1602+ // implicit token
1603+ EXPECT_TRUE(tokens_->Acquire());
1604+}
1605--- /dev/null
1606+++ b/src/tokenpool-gnu-make-posix.cc
1607@@ -0,0 +1,214 @@
1608+// Copyright 2016-2018 Google Inc. All Rights Reserved.
1609+//
1610+// Licensed under the Apache License, Version 2.0 (the "License");
1611+// you may not use this file except in compliance with the License.
1612+// You may obtain a copy of the License at
1613+//
1614+// http://www.apache.org/licenses/LICENSE-2.0
1615+//
1616+// Unless required by applicable law or agreed to in writing, software
1617+// distributed under the License is distributed on an "AS IS" BASIS,
1618+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1619+// See the License for the specific language governing permissions and
1620+// limitations under the License.
1621+
1622+#include "tokenpool-gnu-make.h"
1623+
1624+#include <errno.h>
1625+#include <fcntl.h>
1626+#include <poll.h>
1627+#include <unistd.h>
1628+#include <signal.h>
1629+#include <sys/time.h>
1630+#include <stdio.h>
1631+#include <string.h>
1632+#include <stdlib.h>
1633+#include <stack>
1634+
1635+// TokenPool implementation for GNU make jobserver - POSIX implementation
1636+// (http://make.mad-scientist.net/papers/jobserver-implementation/)
1637+struct GNUmakeTokenPoolPosix : public GNUmakeTokenPool {
1638+ GNUmakeTokenPoolPosix();
1639+ virtual ~GNUmakeTokenPoolPosix();
1640+
1641+ virtual int GetMonitorFd();
1642+
1643+ virtual const char* GetEnv(const char* name) { return getenv(name); };
1644+ virtual bool ParseAuth(const char* jobserver);
1645+ virtual bool AcquireToken();
1646+ virtual bool ReturnToken();
1647+
1648+ private:
1649+ int rfd_;
1650+ int wfd_;
1651+
1652+ struct sigaction old_act_;
1653+ bool restore_;
1654+
1655+ // See https://www.gnu.org/software/make/manual/html_node/POSIX-Jobserver.html
1656+ //
1657+ // It’s important that when you release the job slot, you write back
1658+ // the same character you read. Don’t assume that all tokens are the
1659+ // same character different characters may have different meanings to
1660+ // GNU make. The order is not important, since make has no idea in
1661+ // what order jobs will complete anyway.
1662+ //
1663+ std::stack<char> tokens_;
1664+
1665+ static int dup_rfd_;
1666+ static void CloseDupRfd(int signum);
1667+
1668+ bool CheckFd(int fd);
1669+ bool SetAlarmHandler();
1670+};
1671+
1672+GNUmakeTokenPoolPosix::GNUmakeTokenPoolPosix() : rfd_(-1), wfd_(-1), restore_(false) {
1673+}
1674+
1675+GNUmakeTokenPoolPosix::~GNUmakeTokenPoolPosix() {
1676+ Clear();
1677+ if (restore_)
1678+ sigaction(SIGALRM, &old_act_, NULL);
1679+}
1680+
1681+bool GNUmakeTokenPoolPosix::CheckFd(int fd) {
1682+ if (fd < 0)
1683+ return false;
1684+ int ret = fcntl(fd, F_GETFD);
1685+ return ret >= 0;
1686+}
1687+
1688+int GNUmakeTokenPoolPosix::dup_rfd_ = -1;
1689+
1690+void GNUmakeTokenPoolPosix::CloseDupRfd(int signum) {
1691+ close(dup_rfd_);
1692+ dup_rfd_ = -1;
1693+}
1694+
1695+bool GNUmakeTokenPoolPosix::SetAlarmHandler() {
1696+ struct sigaction act;
1697+ memset(&act, 0, sizeof(act));
1698+ act.sa_handler = CloseDupRfd;
1699+ if (sigaction(SIGALRM, &act, &old_act_) < 0) {
1700+ perror("sigaction:");
1701+ return false;
1702+ }
1703+ restore_ = true;
1704+ return true;
1705+}
1706+
1707+bool GNUmakeTokenPoolPosix::ParseAuth(const char* jobserver) {
1708+ int rfd = -1;
1709+ int wfd = -1;
1710+ if ((sscanf(jobserver, "%*[^=]=%d,%d", &rfd, &wfd) == 2) &&
1711+ CheckFd(rfd) &&
1712+ CheckFd(wfd) &&
1713+ SetAlarmHandler()) {
1714+ rfd_ = rfd;
1715+ wfd_ = wfd;
1716+ return true;
1717+ }
1718+
1719+ return false;
1720+}
1721+
1722+bool GNUmakeTokenPoolPosix::AcquireToken() {
1723+ // Please read
1724+ //
1725+ // http://make.mad-scientist.net/papers/jobserver-implementation/
1726+ //
1727+ // for the reasoning behind the following code.
1728+ //
1729+ // Try to read one character from the pipe. Returns true on success.
1730+ //
1731+ // First check if read() would succeed without blocking.
1732+#ifdef USE_PPOLL
1733+ pollfd pollfds[] = {{rfd_, POLLIN, 0}};
1734+ int ret = poll(pollfds, 1, 0);
1735+#else
1736+ fd_set set;
1737+ struct timeval timeout = { 0, 0 };
1738+ FD_ZERO(&set);
1739+ FD_SET(rfd_, &set);
1740+ int ret = select(rfd_ + 1, &set, NULL, NULL, &timeout);
1741+#endif
1742+ if (ret > 0) {
1743+ // Handle potential race condition:
1744+ // - the above check succeeded, i.e. read() should not block
1745+ // - the character disappears before we call read()
1746+ //
1747+ // Create a duplicate of rfd_. The duplicate file descriptor dup_rfd_
1748+ // can safely be closed by signal handlers without affecting rfd_.
1749+ dup_rfd_ = dup(rfd_);
1750+
1751+ if (dup_rfd_ != -1) {
1752+ struct sigaction act, old_act;
1753+ int ret = 0;
1754+ char buf;
1755+
1756+ // Temporarily replace SIGCHLD handler with our own
1757+ memset(&act, 0, sizeof(act));
1758+ act.sa_handler = CloseDupRfd;
1759+ if (sigaction(SIGCHLD, &act, &old_act) == 0) {
1760+ struct itimerval timeout;
1761+
1762+ // install a 100ms timeout that generates SIGALARM on expiration
1763+ memset(&timeout, 0, sizeof(timeout));
1764+ timeout.it_value.tv_usec = 100 * 1000; // [ms] -> [usec]
1765+ if (setitimer(ITIMER_REAL, &timeout, NULL) == 0) {
1766+ // Now try to read() from dup_rfd_. Return values from read():
1767+ //
1768+ // 1. token read -> 1
1769+ // 2. pipe closed -> 0
1770+ // 3. alarm expires -> -1 (EINTR)
1771+ // 4. child exits -> -1 (EINTR)
1772+ // 5. alarm expired before entering read() -> -1 (EBADF)
1773+ // 6. child exited before entering read() -> -1 (EBADF)
1774+ // 7. child exited before handler is installed -> go to 1 - 3
1775+ ret = read(dup_rfd_, &buf, 1);
1776+
1777+ // disarm timer
1778+ memset(&timeout, 0, sizeof(timeout));
1779+ setitimer(ITIMER_REAL, &timeout, NULL);
1780+ }
1781+
1782+ sigaction(SIGCHLD, &old_act, NULL);
1783+ }
1784+
1785+ CloseDupRfd(0);
1786+
1787+ // Case 1 from above list
1788+ if (ret > 0) {
1789+ tokens_.push(buf);
1790+ return true;
1791+ }
1792+ }
1793+ }
1794+
1795+ // read() would block, i.e. no token available,
1796+ // cases 2-6 from above list or
1797+ // select() / poll() / dup() / sigaction() / setitimer() failed
1798+ return false;
1799+}
1800+
1801+bool GNUmakeTokenPoolPosix::ReturnToken() {
1802+ const char buf = tokens_.top();
1803+ while (1) {
1804+ int ret = write(wfd_, &buf, 1);
1805+ if (ret > 0) {
1806+ tokens_.pop();
1807+ return true;
1808+ }
1809+ if ((ret != -1) || (errno != EINTR))
1810+ return false;
1811+ // write got interrupted - retry
1812+ }
1813+}
1814+
1815+int GNUmakeTokenPoolPosix::GetMonitorFd() {
1816+ return rfd_;
1817+}
1818+
1819+TokenPool* TokenPool::Get() {
1820+ return new GNUmakeTokenPoolPosix;
1821+}
1822--- /dev/null
1823+++ b/src/tokenpool-gnu-make-win32.cc
1824@@ -0,0 +1,239 @@
1825+// Copyright 2018 Google Inc. All Rights Reserved.
1826+//
1827+// Licensed under the Apache License, Version 2.0 (the "License");
1828+// you may not use this file except in compliance with the License.
1829+// You may obtain a copy of the License at
1830+//
1831+// http://www.apache.org/licenses/LICENSE-2.0
1832+//
1833+// Unless required by applicable law or agreed to in writing, software
1834+// distributed under the License is distributed on an "AS IS" BASIS,
1835+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1836+// See the License for the specific language governing permissions and
1837+// limitations under the License.
1838+
1839+#include "tokenpool-gnu-make.h"
1840+
1841+// Always include this first.
1842+// Otherwise the other system headers don't work correctly under Win32
1843+#include <windows.h>
1844+
1845+#include <ctype.h>
1846+#include <stdlib.h>
1847+#include <string.h>
1848+
1849+#include "util.h"
1850+
1851+// TokenPool implementation for GNU make jobserver - Win32 implementation
1852+// (https://www.gnu.org/software/make/manual/html_node/Windows-Jobserver.html)
1853+struct GNUmakeTokenPoolWin32 : public GNUmakeTokenPool {
1854+ GNUmakeTokenPoolWin32();
1855+ virtual ~GNUmakeTokenPoolWin32();
1856+
1857+ virtual void WaitForTokenAvailability(HANDLE ioport);
1858+ virtual bool TokenIsAvailable(ULONG_PTR key);
1859+
1860+ virtual const char* GetEnv(const char* name);
1861+ virtual bool ParseAuth(const char* jobserver);
1862+ virtual bool AcquireToken();
1863+ virtual bool ReturnToken();
1864+
1865+ private:
1866+ // Semaphore for GNU make jobserver protocol
1867+ HANDLE semaphore_jobserver_;
1868+ // Semaphore Child -> Parent
1869+ // - child releases it before entering wait on jobserver semaphore
1870+ // - parent blocks on it to know when child enters wait
1871+ HANDLE semaphore_enter_wait_;
1872+ // Semaphore Parent -> Child
1873+ // - parent releases it to allow child to restart loop
1874+ // - child blocks on it to know when to restart loop
1875+ HANDLE semaphore_restart_;
1876+ // set to false if child should exit loop and terminate thread
1877+ bool running_;
1878+ // child thread
1879+ HANDLE child_;
1880+ // I/O completion port from SubprocessSet
1881+ HANDLE ioport_;
1882+
1883+
1884+ DWORD SemaphoreThread();
1885+ void ReleaseSemaphore(HANDLE semaphore);
1886+ void WaitForObject(HANDLE object);
1887+ static DWORD WINAPI SemaphoreThreadWrapper(LPVOID param);
1888+ static void NoopAPCFunc(ULONG_PTR param);
1889+};
1890+
1891+GNUmakeTokenPoolWin32::GNUmakeTokenPoolWin32() : semaphore_jobserver_(NULL),
1892+ semaphore_enter_wait_(NULL),
1893+ semaphore_restart_(NULL),
1894+ running_(false),
1895+ child_(NULL),
1896+ ioport_(NULL) {
1897+}
1898+
1899+GNUmakeTokenPoolWin32::~GNUmakeTokenPoolWin32() {
1900+ Clear();
1901+ CloseHandle(semaphore_jobserver_);
1902+ semaphore_jobserver_ = NULL;
1903+
1904+ if (child_) {
1905+ // tell child thread to exit
1906+ running_ = false;
1907+ ReleaseSemaphore(semaphore_restart_);
1908+
1909+ // wait for child thread to exit
1910+ WaitForObject(child_);
1911+ CloseHandle(child_);
1912+ child_ = NULL;
1913+ }
1914+
1915+ if (semaphore_restart_) {
1916+ CloseHandle(semaphore_restart_);
1917+ semaphore_restart_ = NULL;
1918+ }
1919+
1920+ if (semaphore_enter_wait_) {
1921+ CloseHandle(semaphore_enter_wait_);
1922+ semaphore_enter_wait_ = NULL;
1923+ }
1924+}
1925+
1926+const char* GNUmakeTokenPoolWin32::GetEnv(const char* name) {
1927+ // getenv() does not work correctly together with tokenpool_tests.cc
1928+ static char buffer[MAX_PATH + 1];
1929+ if (GetEnvironmentVariable(name, buffer, sizeof(buffer)) == 0)
1930+ return NULL;
1931+ return buffer;
1932+}
1933+
1934+bool GNUmakeTokenPoolWin32::ParseAuth(const char* jobserver) {
1935+ // match "--jobserver-auth=gmake_semaphore_<INTEGER>..."
1936+ const char* start = strchr(jobserver, '=');
1937+ if (start) {
1938+ const char* end = start;
1939+ unsigned int len;
1940+ char c, *auth;
1941+
1942+ while ((c = *++end) != '\0')
1943+ if (!(isalnum(c) || (c == '_')))
1944+ break;
1945+ len = end - start; // includes string terminator in count
1946+
1947+ if ((len > 1) && ((auth = (char*)malloc(len)) != NULL)) {
1948+ strncpy(auth, start + 1, len - 1);
1949+ auth[len - 1] = '\0';
1950+
1951+ if ((semaphore_jobserver_ =
1952+ OpenSemaphore(SEMAPHORE_ALL_ACCESS, /* Semaphore access setting */
1953+ FALSE, /* Child processes DON'T inherit */
1954+ auth /* Semaphore name */
1955+ )) != NULL) {
1956+ free(auth);
1957+ return true;
1958+ }
1959+
1960+ free(auth);
1961+ }
1962+ }
1963+
1964+ return false;
1965+}
1966+
1967+bool GNUmakeTokenPoolWin32::AcquireToken() {
1968+ return WaitForSingleObject(semaphore_jobserver_, 0) == WAIT_OBJECT_0;
1969+}
1970+
1971+bool GNUmakeTokenPoolWin32::ReturnToken() {
1972+ ReleaseSemaphore(semaphore_jobserver_);
1973+ return true;
1974+}
1975+
1976+DWORD GNUmakeTokenPoolWin32::SemaphoreThread() {
1977+ while (running_) {
1978+ // indicate to parent that we are entering wait
1979+ ReleaseSemaphore(semaphore_enter_wait_);
1980+
1981+ // alertable wait forever on token semaphore
1982+ if (WaitForSingleObjectEx(semaphore_jobserver_, INFINITE, TRUE) == WAIT_OBJECT_0) {
1983+ // release token again for AcquireToken()
1984+ ReleaseSemaphore(semaphore_jobserver_);
1985+
1986+ // indicate to parent on ioport that a token might be available
1987+ if (!PostQueuedCompletionStatus(ioport_, 0, (ULONG_PTR) this, NULL))
1988+ Win32Fatal("PostQueuedCompletionStatus");
1989+ }
1990+
1991+ // wait for parent to allow loop restart
1992+ WaitForObject(semaphore_restart_);
1993+ // semaphore is now in nonsignaled state again for next run...
1994+ }
1995+
1996+ return 0;
1997+}
1998+
1999+DWORD WINAPI GNUmakeTokenPoolWin32::SemaphoreThreadWrapper(LPVOID param) {
2000+ GNUmakeTokenPoolWin32* This = (GNUmakeTokenPoolWin32*) param;
2001+ return This->SemaphoreThread();
2002+}
2003+
2004+void GNUmakeTokenPoolWin32::NoopAPCFunc(ULONG_PTR param) {
2005+}
2006+
2007+void GNUmakeTokenPoolWin32::WaitForTokenAvailability(HANDLE ioport) {
2008+ if (child_ == NULL) {
2009+ // first invocation
2010+ //
2011+ // subprocess-win32.cc uses I/O completion port (IOCP) which can't be
2012+ // used as a waitable object. Therefore we can't use WaitMultipleObjects()
2013+ // to wait on the IOCP and the token semaphore at the same time. Create
2014+ // a child thread that waits on the semaphore and posts an I/O completion
2015+ ioport_ = ioport;
2016+
2017+ // create both semaphores in nonsignaled state
2018+ if ((semaphore_enter_wait_ = CreateSemaphore(NULL, 0, 1, NULL))
2019+ == NULL)
2020+ Win32Fatal("CreateSemaphore/enter_wait");
2021+ if ((semaphore_restart_ = CreateSemaphore(NULL, 0, 1, NULL))
2022+ == NULL)
2023+ Win32Fatal("CreateSemaphore/restart");
2024+
2025+ // start child thread
2026+ running_ = true;
2027+ if ((child_ = CreateThread(NULL, 0, &SemaphoreThreadWrapper, this, 0, NULL))
2028+ == NULL)
2029+ Win32Fatal("CreateThread");
2030+
2031+ } else {
2032+ // all further invocations - allow child thread to loop
2033+ ReleaseSemaphore(semaphore_restart_);
2034+ }
2035+
2036+ // wait for child thread to enter wait
2037+ WaitForObject(semaphore_enter_wait_);
2038+ // semaphore is now in nonsignaled state again for next run...
2039+
2040+ // now SubprocessSet::DoWork() can enter GetQueuedCompletionStatus()...
2041+}
2042+
2043+bool GNUmakeTokenPoolWin32::TokenIsAvailable(ULONG_PTR key) {
2044+ // alert child thread to break wait on token semaphore
2045+ QueueUserAPC((PAPCFUNC)&NoopAPCFunc, child_, (ULONG_PTR)NULL);
2046+
2047+ // return true when GetQueuedCompletionStatus() returned our key
2048+ return key == (ULONG_PTR) this;
2049+}
2050+
2051+void GNUmakeTokenPoolWin32::ReleaseSemaphore(HANDLE semaphore) {
2052+ if (!::ReleaseSemaphore(semaphore, 1, NULL))
2053+ Win32Fatal("ReleaseSemaphore");
2054+}
2055+
2056+void GNUmakeTokenPoolWin32::WaitForObject(HANDLE object) {
2057+ if (WaitForSingleObject(object, INFINITE) != WAIT_OBJECT_0)
2058+ Win32Fatal("WaitForSingleObject");
2059+}
2060+
2061+TokenPool* TokenPool::Get() {
2062+ return new GNUmakeTokenPoolWin32;
2063+}
2064--- /dev/null
2065+++ b/src/tokenpool-gnu-make.h
2066@@ -0,0 +1,40 @@
2067+// Copyright 2016-2018 Google Inc. All Rights Reserved.
2068+//
2069+// Licensed under the Apache License, Version 2.0 (the "License");
2070+// you may not use this file except in compliance with the License.
2071+// You may obtain a copy of the License at
2072+//
2073+// http://www.apache.org/licenses/LICENSE-2.0
2074+//
2075+// Unless required by applicable law or agreed to in writing, software
2076+// distributed under the License is distributed on an "AS IS" BASIS,
2077+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2078+// See the License for the specific language governing permissions and
2079+// limitations under the License.
2080+
2081+#include "tokenpool.h"
2082+
2083+// interface to GNU make token pool
2084+struct GNUmakeTokenPool : public TokenPool {
2085+ GNUmakeTokenPool();
2086+ ~GNUmakeTokenPool();
2087+
2088+ // token pool implementation
2089+ virtual bool Acquire();
2090+ virtual void Reserve();
2091+ virtual void Release();
2092+ virtual void Clear();
2093+ virtual bool Setup(bool ignore, bool verbose, double& max_load_average);
2094+
2095+ // platform specific implementation
2096+ virtual const char* GetEnv(const char* name) = 0;
2097+ virtual bool ParseAuth(const char* jobserver) = 0;
2098+ virtual bool AcquireToken() = 0;
2099+ virtual bool ReturnToken() = 0;
2100+
2101+ private:
2102+ int available_;
2103+ int used_;
2104+
2105+ void Return();
2106+};
2107--- a/CMakeLists.txt
2108+++ b/CMakeLists.txt
2109@@ -142,6 +142,7 @@ add_library(libninja OBJECT
2110 src/state.cc
2111 src/status.cc
2112 src/string_piece_util.cc
2113+ src/tokenpool-gnu-make.cc
2114 src/util.cc
2115 src/version.cc
2116 )
2117@@ -153,13 +154,17 @@ if(WIN32)
2118 src/msvc_helper_main-win32.cc
2119 src/getopt.c
2120 src/minidump-win32.cc
2121+ src/tokenpool-gnu-make-win32.cc
2122 )
2123 # Build getopt.c, which can be compiled as either C or C++, as C++
2124 # so that build environments which lack a C compiler, but have a C++
2125 # compiler may build ninja.
2126 set_source_files_properties(src/getopt.c PROPERTIES LANGUAGE CXX)
2127 else()
2128- target_sources(libninja PRIVATE src/subprocess-posix.cc)
2129+ target_sources(libninja PRIVATE
2130+ src/subprocess-posix.cc
2131+ src/tokenpool-gnu-make-posix.cc
2132+ )
2133 if(CMAKE_SYSTEM_NAME STREQUAL "OS400" OR CMAKE_SYSTEM_NAME STREQUAL "AIX")
2134 target_sources(libninja PRIVATE src/getopt.c)
2135 # Build getopt.c, which can be compiled as either C or C++, as C++
2136@@ -286,6 +291,7 @@ if(BUILD_TESTING)
2137 src/string_piece_util_test.cc
2138 src/subprocess_test.cc
2139 src/test.cc
2140+ src/tokenpool_test.cc
2141 src/util_test.cc
2142 )
2143 if(WIN32)