Files
DarkflameServer/dUgcServer/Processing/UgcThrottle.h
Aaron Kimbrell 6ae6eac64e feat(ugc): LU Toolbox worker in a headless Blender
UgcToolbox runs LU Toolbox itself in an external Blender that stays up
and makes one model after another (dlu_toolbox_worker.py runs
LU-Toolbox-Standalone's steps: import, Process Model, Bake Lighting,
niftools export). JSON lines over its stdin and original stdout; it is
restarted after a crash, a timeout, 100 models or new settings, runs at
worker_nice with toolbox_threads, and its CPU time is charged to the
worker, which pauses it (SIGSTOP) while the CPU budget is overdrawn.
ProcessModelToolbox reads LU Toolbox's .nif back, counts its triangles
and draws the icon from it. Tests: option parsing, the fallback, the
protocol framing, the worker with a stand-in Blender (crash, hang,
failure, give-up), and LU Toolbox itself when DLU_TEST_TOOLBOX_* are set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 16:47:40 -05:00

55 lines
2.4 KiB
C++

#pragma once
#include <cstdint>
#include <functional>
#include <string>
/**
* Keeps the UGC workers' CPU use under a budget. The long loops (renders, ambient occlusion, icons) call Checkpoint
* every few milliseconds of work; it adds the calling thread's CPU time since its last call to a shared account that
* the budget fills at `budget` CPUs per second, and sleeps while the account is overdrawn. So the workers together
* average at most the budget however many there are, long jobs included, and without a budget it costs nothing.
*/
namespace UgcThrottle {
// CPUs the workers may use together on average (1.5: one and a half cores); 0 or less: no limit
void SetBudget(double cpus);
double GetBudget();
// Account the calling thread's CPU time and sleep when over the budget. Throws Cancelled once Cancel(true) was
// called, so a job stops in moments when the server shuts down.
void Checkpoint();
// The same, for work done by another process for this thread (LU Toolbox's Blender, charged with Charge first):
// `pausing` is called with true before the sleep and false after it, to stop that process meanwhile
void Checkpoint(const std::function<void(bool)>& pausing);
// Thrown by Checkpoint while cancelled: the job is abandoned, not failed (its row stays waiting)
struct Cancelled {};
// Stop (true) or allow (false) every job's work: set when the server stops, cleared when it starts
void Cancel(bool cancel);
bool IsCancelled();
// Start accounting on this thread from now (a worker starting a job), so time spent idle isn't counted
void Begin();
struct Stats {
uint64_t sleptMs{}; // since start
int64_t lastSleepUnixMs{}; // when it last slept, 0 never
};
Stats GetStats();
// This thread's CPU time in seconds
double ThreadCpuSeconds();
// CPU time a library spent for this thread on threads of its own (Open Image Denoise's), counted as this thread's:
// by the budget at the next Checkpoint and in JobCpuSeconds
void Charge(double seconds);
// This thread's CPU time with what was charged to it, in seconds (a job's CPU time is the difference)
double JobCpuSeconds();
// Parses "18-23" (from 18:00 to 23:59; may wrap past midnight, "22-6") into from and to; false when it isn't that
bool ParseHours(const std::string& text, int& from, int& to);
// Whether `hour` (0-23) is inside the range; never when from or to is negative
bool InHours(int hour, int from, int to);
}