rate/token_bucket.hpp — a lazily-refilled TokenBucket (starts full: burst
then throttle, IDM behaviour; rate 0 == unlimited; burst caps idle
accumulation) and RateLimiter, the global -> per-queue -> per-task
hierarchy (docs/04 §6). acquire(task, n) peeks every applicable level and
commits on all-or-none so a blocked attempt never leaks tokens at a level
that had them; held under one mutex so a concurrent detach can't dangle
the bucket it's using. vdm/ids.hpp gains QueueId.
Tests: burst/refill/cap/unlimited for the bucket; tightest-level-binds,
no-partial-consumption, detach-safety, and an 8-thread aggregate-rate
check for the hierarchy. Green under ASan/UBSan and TSan.
Engine-API review (DAEMON signed off, no sched/ or dispatch rewrite):
- Engine::rate_limiter() accessor added (limiter.set -> set_global_limit).
- Checksum::Algo gains sha512 to match the wire Checksum set.
- DownloadSpec: DAEMON creates save_path's parent dir before start();
missing dir -> Error::path_rejected (made explicit).
- cancel(): documented to fire on_state(_, cancelled, nullopt) then
on_finished(Err{canceled}), in that order; download.cancel ==
cancel(false), download.remove == cancel(true).
- engine-api-m1.md: the five open questions resolved with DAEMON's
answers (probe_hint optional, single cancel flag, {restart,
keep_partial, abort} is the whole set, per-task 4 Hz is fine,
refresh_url restarts all segments after a validating re-probe).
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HPPSGhiArbvQgwC2DNiURS
55 lines
1.5 KiB
C++
55 lines
1.5 KiB
C++
// vdm/ids.hpp — opaque engine-internal identifiers.
|
|
//
|
|
// TaskId is CORE's handle for a download. DAEMON owns the wire UUID and keeps a
|
|
// UUID <-> TaskId map; CORE never sees the UUID (layering rule — no wire types in the
|
|
// engine). Assigned by CORE when DAEMON registers a task.
|
|
//
|
|
// This header compiles standalone.
|
|
|
|
#ifndef VDM_IDS_HPP
|
|
#define VDM_IDS_HPP
|
|
|
|
#include <chrono>
|
|
#include <compare>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <functional>
|
|
|
|
namespace vdm {
|
|
|
|
struct TaskId {
|
|
std::uint64_t value = 0;
|
|
|
|
[[nodiscard]] constexpr bool valid() const noexcept { return value != 0; }
|
|
friend constexpr auto operator<=>(const TaskId &, const TaskId &) = default;
|
|
};
|
|
|
|
// A scheduler queue. A task may belong to one — for the per-queue rate limit and
|
|
// per-queue concurrency. A task with no queue rate-limits against the global bucket only.
|
|
struct QueueId {
|
|
std::uint64_t value = 0;
|
|
|
|
[[nodiscard]] constexpr bool valid() const noexcept { return value != 0; }
|
|
friend constexpr auto operator<=>(const QueueId &, const QueueId &) = default;
|
|
};
|
|
|
|
using SteadyTime = std::chrono::steady_clock::time_point;
|
|
|
|
} // namespace vdm
|
|
|
|
template <>
|
|
struct std::hash<vdm::TaskId> {
|
|
std::size_t operator()(vdm::TaskId id) const noexcept {
|
|
return std::hash<std::uint64_t>{}(id.value);
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct std::hash<vdm::QueueId> {
|
|
std::size_t operator()(vdm::QueueId id) const noexcept {
|
|
return std::hash<std::uint64_t>{}(id.value);
|
|
}
|
|
};
|
|
|
|
#endif // VDM_IDS_HPP
|