#include #include #include #include #include #include #include #ifndef _WIN32 # include #endif #include "cmGetPipes.h" #include "cmStringAlgorithms.h" #include "cmSystemTools.h" #include "cmUVHandlePtr.h" #include "cmUVJobServerClient.h" namespace { const std::size_t kTOTAL_JOBS = 10; const std::size_t kTOTAL_TOKENS = 3; struct Job { cm::uv_timer_ptr Timer; }; struct JobRunner { cm::uv_loop_ptr Loop; cm::optional JSC; std::vector Jobs; std::size_t NextJobIndex = 0; std::size_t ActiveJobs = 0; std::deque Queue; bool Okay = true; JobRunner() : Jobs(kTOTAL_JOBS) { this->Loop.init(nullptr); this->JSC = cmUVJobServerClient::Connect( *this->Loop, [this]() { this->StartQueuedJob(); }, nullptr); if (!this->JSC) { std::cerr << "Failed to connect to job server.\n"; this->Okay = false; } } ~JobRunner() {} bool Run() { if (this->Okay) { this->QueueNextJobs(); uv_run(this->Loop, UV_RUN_DEFAULT); std::cerr << "HeldTokens: " << this->JSC->GetHeldTokens() << '\n'; std::cerr << "NeedTokens: " << this->JSC->GetNeedTokens() << '\n'; } #ifdef _WIN32 // FIXME: Windows job server client not yet implemented. return true; #else return this->Okay; #endif } void QueueNextJobs() { std::cerr << "QueueNextJobs()\n"; std::size_t queued = 0; while (queued < 2 && this->NextJobIndex < this->Jobs.size()) { this->QueueJob(this->NextJobIndex); ++this->NextJobIndex; ++queued; } std::cerr << "QueueNextJobs done\n"; } void StartQueuedJob() { std::cerr << "StartQueuedJob()\n"; assert(!this->Queue.empty()); std::size_t index = this->Queue.front(); this->Queue.pop_front(); this->StartJob(index); std::cerr << "StartQueuedJob done\n"; } void StartJob(std::size_t index) { cm::uv_timer_ptr& job = this->Jobs[index].Timer; job.init(*this->Loop, this); uv_timer_start( job, [](uv_timer_t* handle) { uv_timer_stop(handle); auto self = static_cast(handle->data); self->FinishJob(); }, /*timeout_ms=*/10 * (1 + (index % 3)), /*repeat_ms=*/0); ++this->ActiveJobs; std::cerr << " StartJob(" << index << "): Active jobs: " << this->ActiveJobs << '\n'; if (this->ActiveJobs > kTOTAL_TOKENS) { std::cerr << "Started more than " << kTOTAL_TOKENS << " jobs at once!\n"; this->Okay = false; return; } } void QueueJob(std::size_t index) { this->JSC->RequestToken(); this->Queue.push_back(index); std::cerr << " QueueJob(" << index << "): Queue length: " << this->Queue.size() << '\n'; } void FinishJob() { --this->ActiveJobs; std::cerr << "FinishJob: Active jobs: " << this->ActiveJobs << '\n'; this->JSC->ReleaseToken(); this->QueueNextJobs(); } }; bool testJobServer() { #ifdef _WIN32 // FIXME: Windows job server client not yet implemented. #else // Create a job server pipe. int jobServerPipe[2]; if (cmGetPipes(jobServerPipe) < 0) { std::cerr << "Failed to create job server pipe\n"; return false; } // Write N-1 tokens to the pipe. std::vector jobServerInit(kTOTAL_TOKENS - 1, '.'); if (write(jobServerPipe[1], jobServerInit.data(), jobServerInit.size()) != kTOTAL_TOKENS - 1) { std::cerr << "Failed to initialize job server pipe\n"; return false; } // Establish the job server client context. // Add a bogus server spec to verify we use the last spec. cmSystemTools::PutEnv(cmStrCat("MAKEFLAGS=--flags-before" " --jobserver-auth=bogus" " --flags-between" " --jobserver-fds=", jobServerPipe[0], ',', jobServerPipe[1], " --flags-after")); #endif JobRunner jobRunner; return jobRunner.Run(); } } int testUVJobServerClient(int, char** const) { bool passed = true; passed = testJobServer() && passed; return passed ? 0 : -1; }