mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 10:53:44 +00:00
The dashboard's web server answers one request at a time, so converting a big .nif (glom files up to tens of MB) held up every other request, flairs included. - dWeb: Web::Defer hands a request to another thread; the reply is sent from the web thread on its next poll (DeferredQueue). A client that leaves first cancels it and the late reply is dropped. The synchronous route API is unchanged. - Web::Shutdown closes connections while the state their close events touch is still alive; the destructor no longer runs handlers during static destruction (stopping the dashboard aborted in ~WSClient). - WorkerPool: priority lanes, with one thread only for urgent work (flairs, small models, textures), and limited background work. - Scenery: mesh and texture routes (and the showcase's) convert on the pool; thread-safe memory and disk caches, one conversion per model at a time with waiters sharing it; zones are converted ahead onto the disk cache while viewed. - Setting scenery_workers (0: half the cores, 2 to 4). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
96 lines
3.0 KiB
C++
96 lines
3.0 KiB
C++
#pragma once
|
|
|
|
#include <array>
|
|
#include <condition_variable>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <deque>
|
|
#include <functional>
|
|
#include <mutex>
|
|
#include <optional>
|
|
#include <thread>
|
|
#include <vector>
|
|
|
|
/**
|
|
* A few worker threads for slow work that routes hand off with Web::Defer (converting the client's models), so the
|
|
* web server's one thread keeps answering. Jobs run by priority, in the order they came within one.
|
|
*
|
|
* The first thread is a fast lane that only runs URGENT jobs, so something small and wanted now (a flair near the
|
|
* camera) never waits behind big conversions filling the other threads. BACKGROUND jobs (converting ahead of time)
|
|
* only run when nothing else waits, and only a few at once, so they never take every thread.
|
|
*/
|
|
class WorkerPool {
|
|
public:
|
|
enum class ePriority : uint8_t {
|
|
URGENT, // small and wanted now: the fast lane takes these too
|
|
NORMAL,
|
|
LARGE, // big jobs someone waits for
|
|
BACKGROUND, // nobody waits for it
|
|
};
|
|
static constexpr size_t PRIORITIES = 4;
|
|
|
|
using Job = std::function<void()>;
|
|
|
|
WorkerPool() = default;
|
|
~WorkerPool() { Stop(); }
|
|
WorkerPool(const WorkerPool&) = delete;
|
|
WorkerPool& operator=(const WorkerPool&) = delete;
|
|
|
|
/**
|
|
* Start `threads` workers (at least 2: the fast lane and one more). At most `maxBackground` BACKGROUND jobs run
|
|
* at once (at least 1).
|
|
*/
|
|
void Start(size_t threads, size_t maxBackground = 1);
|
|
|
|
// Drop the queued jobs and wait for the running ones
|
|
void Stop();
|
|
|
|
bool Running() const { return !m_Threads.empty(); }
|
|
size_t Threads() const { return m_Threads.size(); }
|
|
|
|
/**
|
|
* Queue a job. `group` (0 for none) lets Cancel drop jobs queued together; `front` puts it before the others of
|
|
* its priority. Without threads (not started) the job runs right away on the caller's thread.
|
|
*/
|
|
void Submit(ePriority priority, Job job, uint64_t group = 0, bool front = false);
|
|
|
|
// Drop the queued jobs of `group`; returns how many
|
|
size_t Cancel(uint64_t group);
|
|
|
|
size_t Queued() const;
|
|
size_t Active() const;
|
|
|
|
// Wait until nothing is queued or running (for tests)
|
|
void WaitIdle();
|
|
|
|
/**
|
|
* Which priority a worker takes next from queues of these lengths: the most urgent one waiting, only URGENT for
|
|
* the fast lane, and BACKGROUND only while fewer than maxBackground of those run. nullopt: nothing for it.
|
|
*/
|
|
static std::optional<ePriority> Pick(const std::array<size_t, PRIORITIES>& queued, bool fastLane, size_t runningBackground, size_t maxBackground);
|
|
|
|
/**
|
|
* The default number of threads for this many CPU cores: half of them, from 2 to 4 (a conversion is one core's
|
|
* work, and the game servers on the same machine need the rest)
|
|
*/
|
|
static size_t DefaultThreads(size_t cores);
|
|
|
|
private:
|
|
struct Entry {
|
|
Job job;
|
|
uint64_t group{};
|
|
};
|
|
|
|
void Work(bool fastLane);
|
|
|
|
mutable std::mutex m_Mutex;
|
|
std::condition_variable m_Wake;
|
|
std::condition_variable m_Idle;
|
|
std::array<std::deque<Entry>, PRIORITIES> m_Queues;
|
|
std::vector<std::thread> m_Threads;
|
|
size_t m_MaxBackground{ 1 };
|
|
size_t m_RunningBackground{};
|
|
size_t m_Active{};
|
|
bool m_Stopping{};
|
|
};
|