1
0

100-make_jobserver_support.patch 63 KB

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