100-make_jobserver_support.patch 67 KB

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