blob: 13f0f40c12788d3e9bfdbeb4cde688bacf38e4af [file] [log] [blame]
#include <cassert>
#include <cstddef>
#include <deque>
#include <iostream>
#include <vector>
#include <cm/optional>
#include <cm3p/uv.h>
#ifndef _WIN32
# include <unistd.h>
#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<cmUVJobServerClient> JSC;
std::vector<Job> Jobs;
std::size_t NextJobIndex = 0;
std::size_t ActiveJobs = 0;
std::deque<std::size_t> 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<JobRunner*>(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<char> 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;
}