100-make_jobserver_support.patch 62 KB

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