Files
vdm/core/include/vdm/ids.hpp
T
samiandClaude Sonnet 5 5d81b4cdae core: segment/segmenter + segment/budget (stage 6)
vdm/ids.hpp — TaskId, an opaque engine handle (DAEMON keeps the wire
UUID <-> TaskId map; the engine never sees the UUID).

segment/segmenter — per-download range management (docs/04 §3). Initial
lazy split; assign_slot() splits the largest remaining range when the
budget grants a slot; on_complete(may_steal) either *steals* the second
half of the largest remaining range for the same worker (slot-neutral) or
returns nullopt so the caller *yields* the slot (ADR 0011 A1); on_failed()
returns requeue only on the 3rd consecutive connection error with a mirror
present — the remaining range is orphaned and re-split. Non-resumable or
unknown-size => exactly 1 segment; never split below min_segment_bytes
(1 MiB). Resume ctor rebuilds from a persisted table (falls back to a
fresh layout if it doesn't tile [0,total)). One mutex == "the task lock";
segment fields are std::atomic and the store is a std::deque so a steal's
append never moves a worker's record.

segment/budget — the global allocator (ADR 0011). Owns exactly one
ceiling (maxActiveSegments) and min-1-before-seconds fairness: a two-pass
allocation (guarantee pass gives every wanting task 1 slot in DAEMON's
priority order, then a growth pass round-robins the rest up to each
task's effective cap = min(per_task_cap, host cap, 1 if non-resumable)),
recomputed from scratch on every edge so a live set_max_active_segments
cut naturally yields the excess lowest-priority-first, never a
mid-segment kill. DAEMON-facing surface exactly as promised in
daemon/docs/core-requests-m1.md / ADR 0011: budget(), segments_active(),
starved_tasks(), starved_since(), set_max_active_segments (drain),
set_host_segment_cap, set_task_order, on_budget_changed (a jthread
coalesces at <=4 Hz; the tasks_starved 0<->nonzero edge fires
immediately). Callbacks are copied out and run after the lock is
dropped.

Tests: segmenter split/steal/requeue/resume math + a concurrent
steal-and-advance run; budget min-1 under a tight budget, round-robin
growth, host-cap and non-resumable clamps, live-lower shedding
lowest-priority-first, starvation below the task count, starved-edge
notification, and a concurrent set_want hammer. Green under ASan/UBSan;
the steal path and the budget green under TSan.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HPPSGhiArbvQgwC2DNiURS
2026-09-10 15:14:14 +04:00

39 lines
963 B
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;
};
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);
}
};
#endif // VDM_IDS_HPP