testUVJobServerClient.cxx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. #include <cassert>
  2. #include <cstddef>
  3. #include <deque>
  4. #include <iostream>
  5. #include <vector>
  6. #include <cm/optional>
  7. #include <cm3p/uv.h>
  8. #ifndef _WIN32
  9. # include <unistd.h>
  10. #endif
  11. #include "cmGetPipes.h"
  12. #include "cmStringAlgorithms.h"
  13. #include "cmSystemTools.h"
  14. #include "cmUVHandlePtr.h"
  15. #include "cmUVJobServerClient.h"
  16. namespace {
  17. const std::size_t kTOTAL_JOBS = 10;
  18. const std::size_t kTOTAL_TOKENS = 3;
  19. struct Job
  20. {
  21. cm::uv_timer_ptr Timer;
  22. };
  23. struct JobRunner
  24. {
  25. cm::uv_loop_ptr Loop;
  26. cm::optional<cmUVJobServerClient> JSC;
  27. std::vector<Job> Jobs;
  28. std::size_t NextJobIndex = 0;
  29. std::size_t ActiveJobs = 0;
  30. std::deque<std::size_t> Queue;
  31. bool Okay = true;
  32. JobRunner()
  33. : Jobs(kTOTAL_JOBS)
  34. {
  35. this->Loop.init(nullptr);
  36. this->JSC = cmUVJobServerClient::Connect(
  37. *this->Loop, [this]() { this->StartQueuedJob(); }, nullptr);
  38. if (!this->JSC) {
  39. std::cerr << "Failed to connect to job server.\n";
  40. this->Okay = false;
  41. }
  42. }
  43. ~JobRunner() {}
  44. bool Run()
  45. {
  46. if (this->Okay) {
  47. this->QueueNextJobs();
  48. uv_run(this->Loop, UV_RUN_DEFAULT);
  49. std::cerr << "HeldTokens: " << this->JSC->GetHeldTokens() << '\n';
  50. std::cerr << "NeedTokens: " << this->JSC->GetNeedTokens() << '\n';
  51. }
  52. #ifdef _WIN32
  53. // FIXME: Windows job server client not yet implemented.
  54. return true;
  55. #else
  56. return this->Okay;
  57. #endif
  58. }
  59. void QueueNextJobs()
  60. {
  61. std::cerr << "QueueNextJobs()\n";
  62. std::size_t queued = 0;
  63. while (queued < 2 && this->NextJobIndex < this->Jobs.size()) {
  64. this->QueueJob(this->NextJobIndex);
  65. ++this->NextJobIndex;
  66. ++queued;
  67. }
  68. std::cerr << "QueueNextJobs done\n";
  69. }
  70. void StartQueuedJob()
  71. {
  72. std::cerr << "StartQueuedJob()\n";
  73. assert(!this->Queue.empty());
  74. std::size_t index = this->Queue.front();
  75. this->Queue.pop_front();
  76. this->StartJob(index);
  77. std::cerr << "StartQueuedJob done\n";
  78. }
  79. void StartJob(std::size_t index)
  80. {
  81. cm::uv_timer_ptr& job = this->Jobs[index].Timer;
  82. job.init(*this->Loop, this);
  83. uv_timer_start(
  84. job,
  85. [](uv_timer_t* handle) {
  86. uv_timer_stop(handle);
  87. auto self = static_cast<JobRunner*>(handle->data);
  88. self->FinishJob();
  89. },
  90. /*timeout_ms=*/10 * (1 + (index % 3)), /*repeat_ms=*/0);
  91. ++this->ActiveJobs;
  92. std::cerr << " StartJob(" << index
  93. << "): Active jobs: " << this->ActiveJobs << '\n';
  94. if (this->ActiveJobs > kTOTAL_TOKENS) {
  95. std::cerr << "Started more than " << kTOTAL_TOKENS << " jobs at once!\n";
  96. this->Okay = false;
  97. return;
  98. }
  99. }
  100. void QueueJob(std::size_t index)
  101. {
  102. this->JSC->RequestToken();
  103. this->Queue.push_back(index);
  104. std::cerr << " QueueJob(" << index
  105. << "): Queue length: " << this->Queue.size() << '\n';
  106. }
  107. void FinishJob()
  108. {
  109. --this->ActiveJobs;
  110. std::cerr << "FinishJob: Active jobs: " << this->ActiveJobs << '\n';
  111. this->JSC->ReleaseToken();
  112. this->QueueNextJobs();
  113. }
  114. };
  115. bool testJobServer()
  116. {
  117. #ifdef _WIN32
  118. // FIXME: Windows job server client not yet implemented.
  119. #else
  120. // Create a job server pipe.
  121. int jobServerPipe[2];
  122. if (cmGetPipes(jobServerPipe) < 0) {
  123. std::cerr << "Failed to create job server pipe\n";
  124. return false;
  125. }
  126. // Write N-1 tokens to the pipe.
  127. std::vector<char> jobServerInit(kTOTAL_TOKENS - 1, '.');
  128. if (write(jobServerPipe[1], jobServerInit.data(), jobServerInit.size()) !=
  129. kTOTAL_TOKENS - 1) {
  130. std::cerr << "Failed to initialize job server pipe\n";
  131. return false;
  132. }
  133. // Establish the job server client context.
  134. // Add a bogus server spec to verify we use the last spec.
  135. cmSystemTools::PutEnv(cmStrCat("MAKEFLAGS=--flags-before"
  136. " --jobserver-auth=bogus"
  137. " --flags-between"
  138. " --jobserver-fds=",
  139. jobServerPipe[0], ',', jobServerPipe[1],
  140. " --flags-after"));
  141. #endif
  142. JobRunner jobRunner;
  143. return jobRunner.Run();
  144. }
  145. }
  146. int testUVJobServerClient(int, char** const)
  147. {
  148. bool passed = true;
  149. passed = testJobServer() && passed;
  150. return passed ? 0 : -1;
  151. }