You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
180 lines
4.1 KiB
180 lines
4.1 KiB
9 months ago
|
#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;
|
||
|
}
|