use std::collections::VecDeque;
use ts_wire::{
control::{
DestroyWeightsUpdateGroupReqOutput, InitWeightsUpdateGroupReqOutput,
IsSchedulerPausedReqOutput, IsSleepingReqOutput, PauseSchedulerReqOutput,
RebalanceExpertsReqOutput, ReleaseMemoryOccupationReqOutput,
ResumeMemoryOccupationReqOutput, ResumeSchedulerReqOutput, UpdateWeightFromDiskReqOutput,
UpdateWeightsFromDistributedReqOutput, UpdateWeightsFromMooncakeReqOutput,
},
ControlOutput, ControlRequest,
};
pub const MEMORY_REGIONS: [&str; 4] = ["kv_cache", "weights", "cuda_graph", "nccl"];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PauseState {
Unpaused,
PausedNew,
PausedAll,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Drain {
Pause,
Release(Vec<String>),
}
#[derive(Debug, Clone)]
pub struct ControlConfig {
pub memory_saver: bool,
pub memory_regions: Vec<String>,
pub l3_weight_version: Option<String>,
}
const L3_FLUSH_REQUIRES_WEIGHT_VERSION: &str = "L3 flushed updates require weight_version so \
independent replicas cannot restore another checkpoint's objects";
fn requested_weight_version(op: &ControlRequest) -> Option<String> {
match op {
ControlRequest::UpdateWeightsFromMooncake(op) => match &op.weight_version {
Some(version) => Some(version.clone()),
None if op.flush_cache => Some(op.version.to_string()),
None => None,
},
ControlRequest::UpdateWeightsFromDistributed(op) => op.weight_version.clone(),
ControlRequest::UpdateWeightFromDisk(op) => op.weight_version.clone(),
_ => None,
}
}
pub fn is_weight_load(op: &ControlRequest) -> bool {
matches!(
op,
ControlRequest::UpdateWeightsFromDistributed(_)
| ControlRequest::UpdateWeightsFromMooncake(_)
| ControlRequest::UpdateWeightFromDisk(_)
)
}
pub fn wants_flush(op: &ControlRequest) -> bool {
match op {
ControlRequest::UpdateWeightsFromDistributed(op) => op.flush_cache,
ControlRequest::UpdateWeightsFromMooncake(op) => op.flush_cache,
ControlRequest::UpdateWeightFromDisk(op) => op.flush_cache,
_ => false,
}
}
pub fn gate_code(op: &ControlRequest) -> Option<i64> {
Some(match op {
ControlRequest::InitWeightsUpdateGroup(_) => 1,
ControlRequest::UpdateWeightsFromDistributed(_) => 2,
ControlRequest::DestroyWeightsUpdateGroup(_) => 3,
ControlRequest::UpdateWeightsFromMooncake(_) => 4,
ControlRequest::RebalanceExperts(_) => 5,
ControlRequest::UpdateWeightFromDisk(_) => 6,
_ => return None,
})
}
pub fn gated_reply(op: &ControlRequest, success: bool, message: String) -> ControlOutput {
match op {
ControlRequest::InitWeightsUpdateGroup(_) => {
ControlOutput::InitWeightsUpdateGroup(InitWeightsUpdateGroupReqOutput {
rid: None,
http_worker_ipc: None,
success,
message,
})
}
ControlRequest::UpdateWeightsFromDistributed(_) => {
ControlOutput::UpdateWeightsFromDistributed(UpdateWeightsFromDistributedReqOutput {
rid: None,
http_worker_ipc: None,
success,
message,
})
}
ControlRequest::DestroyWeightsUpdateGroup(_) => {
ControlOutput::DestroyWeightsUpdateGroup(DestroyWeightsUpdateGroupReqOutput {
rid: None,
http_worker_ipc: None,
success,
message,
})
}
ControlRequest::UpdateWeightsFromMooncake(_) => {
ControlOutput::UpdateWeightsFromMooncake(UpdateWeightsFromMooncakeReqOutput {
rid: None,
http_worker_ipc: None,
success,
message,
})
}
ControlRequest::RebalanceExperts(_) => {
ControlOutput::RebalanceExperts(RebalanceExpertsReqOutput {
rid: None,
http_worker_ipc: None,
success,
message,
})
}
ControlRequest::UpdateWeightFromDisk(_) => {
ControlOutput::UpdateWeightFromDisk(UpdateWeightFromDiskReqOutput {
rid: None,
http_worker_ipc: None,
success,
message,
num_paused_requests: Some(0),
})
}
other => unreachable!("{} is not gated", other.tag()),
}
}
fn pause_reply(success: bool, message: &str) -> ControlOutput {
ControlOutput::PauseScheduler(PauseSchedulerReqOutput {
rid: None,
http_worker_ipc: None,
success,
message: message.to_owned(),
})
}
fn resume_reply(success: bool, message: &str) -> ControlOutput {
ControlOutput::ResumeScheduler(ResumeSchedulerReqOutput {
rid: None,
http_worker_ipc: None,
success,
message: message.to_owned(),
})
}
fn release_reply(success: bool, message: &str) -> ControlOutput {
ControlOutput::ReleaseMemoryOccupation(ReleaseMemoryOccupationReqOutput {
rid: None,
http_worker_ipc: None,
success,
message: message.to_owned(),
})
}
fn resume_memory_reply(success: bool, message: &str) -> ControlOutput {
ControlOutput::ResumeMemoryOccupation(ResumeMemoryOccupationReqOutput {
rid: None,
http_worker_ipc: None,
success,
message: message.to_owned(),
})
}
#[derive(Debug)]
pub struct Controls {
config: ControlConfig,
state: PauseState,
drain: Option<Drain>,
released: Vec<String>,
release_pending: bool,
abort_all: bool,
cancel_unscheduled: bool,
weight_ops: VecDeque<ControlRequest>,
flushes: usize,
replies: Vec<ControlOutput>,
l3_weight_version: Option<String>,
internal_ops: VecDeque<crate::eplb::InternalOp>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Effect {
None,
Profile,
ResumeMemory(Vec<String>),
}
impl Controls {
pub fn new(config: ControlConfig) -> Self {
Self {
state: PauseState::Unpaused,
drain: None,
released: Vec::new(),
release_pending: false,
abort_all: false,
cancel_unscheduled: false,
weight_ops: VecDeque::new(),
flushes: 0,
replies: Vec::new(),
l3_weight_version: config.l3_weight_version.clone(),
internal_ops: VecDeque::new(),
config,
}
}
fn l3_version_refusal(&self, op: &ControlRequest) -> Option<String> {
let current = self.l3_weight_version.as_ref()?;
let requested = requested_weight_version(op);
if wants_flush(op) {
return requested
.is_none()
.then(|| L3_FLUSH_REQUIRES_WEIGHT_VERSION.to_owned());
}
match requested {
Some(version) if version != *current => Some(
"L3 weight_version cannot change without flush_cache; retry the update with \
flush_cache=True so Device/Host KV and in-flight writebacks cannot land in the \
new namespace"
.to_owned(),
),
_ => None,
}
}
pub fn commit_l3_weight_version(
&mut self,
op: &ControlRequest,
) -> Result<Option<String>, String> {
if self.l3_weight_version.is_none() {
return Ok(None);
}
if let Some(refusal) = self.l3_version_refusal(op) {
return Err(refusal);
}
match requested_weight_version(op) {
Some(version) if self.l3_weight_version.as_ref() != Some(&version) => {
self.l3_weight_version = Some(version.clone());
Ok(Some(version))
}
_ => Ok(None),
}
}
pub fn admit_blocked(&self) -> bool {
self.state != PauseState::Unpaused
}
pub fn forward_blocked(&self) -> bool {
self.state == PauseState::PausedAll
}
pub fn memory_released(&self) -> bool {
!self.released.is_empty()
}
pub fn releasing_or_released(&self) -> bool {
self.release_pending || !self.released.is_empty()
}
pub fn take_abort_all(&mut self) -> bool {
std::mem::take(&mut self.abort_all)
}
pub fn take_cancel_unscheduled(&mut self) -> bool {
std::mem::take(&mut self.cancel_unscheduled)
}
pub fn take_replies(&mut self) -> Vec<ControlOutput> {
std::mem::take(&mut self.replies)
}
pub fn reply(&mut self, output: ControlOutput) {
self.replies.push(output);
}
pub fn take_flushes(&mut self) -> usize {
std::mem::take(&mut self.flushes)
}
pub fn weight_head(&self) -> Option<&ControlRequest> {
self.weight_ops.front()
}
pub fn pop_weight_op(&mut self) -> Option<ControlRequest> {
self.weight_ops.pop_front()
}
pub fn weights_busy(&self) -> bool {
!self.weight_ops.is_empty() || !self.internal_ops.is_empty()
}
pub fn enqueue_internal(&mut self, ops: impl IntoIterator<Item = crate::eplb::InternalOp>) {
self.internal_ops.extend(ops);
}
pub fn internal_head(&self) -> Option<&crate::eplb::InternalOp> {
self.internal_ops.front()
}
pub fn pop_internal(&mut self) -> Option<crate::eplb::InternalOp> {
self.internal_ops.pop_front()
}
pub fn drain(&self) -> Option<&Drain> {
self.drain.as_ref()
}
fn request_drain(&mut self, abort_inflight: bool, action: Drain) {
if self.released.is_empty() {
self.state = PauseState::PausedNew;
}
self.drain = Some(action);
self.cancel_unscheduled = true;
if abort_inflight {
self.abort_all = true;
}
}
pub fn take_drain(&mut self) -> Option<Drain> {
self.drain.take()
}
pub fn finish_pause_drain(&mut self) {
self.reply(pause_reply(true, ""));
}
pub fn finish_release(&mut self, tags: Vec<String>) {
self.release_pending = false;
for tag in tags {
if !self.released.contains(&tag) {
self.released.push(tag);
}
}
self.state = PauseState::PausedAll;
self.reply(release_reply(true, ""));
}
pub fn finish_resume(&mut self, tags: &[String]) {
self.released.retain(|tag| !tags.contains(tag));
if self.released.is_empty() {
self.state = PauseState::Unpaused;
}
self.reply(resume_memory_reply(true, ""));
}
fn refuse_weight_load(&self) -> Option<String> {
let released: Vec<&str> = ["weights", "nccl"]
.into_iter()
.filter(|tag| self.released.iter().any(|released| released == tag))
.collect();
(!released.is_empty()).then(|| {
format!(
"memory regions {released:?} are released; resume them with \
resume_memory_occupation before updating weights"
)
})
}
fn normalize_tags(
&self,
tags: Option<&[String]>,
order: &[&str],
) -> Result<Vec<String>, String> {
let available = &self.config.memory_regions;
let Some(tags) = tags else {
return Ok(order
.iter()
.filter(|tag| available.iter().any(|a| a == *tag))
.map(|tag| (*tag).to_owned())
.collect());
};
let bad: Vec<&String> = tags
.iter()
.filter(|tag| !MEMORY_REGIONS.contains(&tag.as_str()))
.collect();
if !bad.is_empty() {
return Err(format!("invalid tags: {bad:?}; valid: {MEMORY_REGIONS:?}"));
}
let unavailable: Vec<&String> =
tags.iter().filter(|tag| !available.contains(tag)).collect();
if !unavailable.is_empty() {
let releasable: Vec<&str> = MEMORY_REGIONS
.into_iter()
.filter(|tag| available.iter().any(|a| a == tag))
.collect();
return Err(format!(
"tags not releasable on this engine: {unavailable:?}; available: {releasable:?}"
));
}
Ok(order
.iter()
.filter(|tag| tags.iter().any(|t| t == *tag))
.map(|tag| (*tag).to_owned())
.collect())
}
pub fn handle(&mut self, request: ControlRequest, release_refusal: Option<String>) -> Effect {
match request {
ControlRequest::FlushCache(_) => self.flushes += 1,
ControlRequest::PauseScheduler(pause) => self.pause(&pause.mode),
ControlRequest::ResumeScheduler(_) => self.resume(),
ControlRequest::IsSchedulerPaused(_) => {
let is_paused = self.state != PauseState::Unpaused;
self.reply(ControlOutput::IsSchedulerPaused(
IsSchedulerPausedReqOutput {
rid: None,
http_worker_ipc: None,
is_paused,
},
));
}
ControlRequest::ReleaseMemoryOccupation(release) => {
self.release(release.tags.as_deref(), release_refusal);
}
ControlRequest::ResumeMemoryOccupation(resume) => {
return self.resume_memory(resume.tags.as_deref());
}
ControlRequest::IsSleeping(_) => {
let is_sleeping = !self.released.is_empty();
self.reply(ControlOutput::IsSleeping(IsSleepingReqOutput {
rid: None,
http_worker_ipc: None,
is_sleeping,
}));
}
ControlRequest::GetInternalState(_) => {
self.reply(ControlOutput::GetInternalState(
ts_wire::control::GetInternalStateReqOutput {
rid: None,
http_worker_ipc: None,
internal_state: ts_wire::Dict(Vec::new()),
},
));
}
ControlRequest::SetInternalState(_) => {
self.reply(ControlOutput::SetInternalState(
ts_wire::control::SetInternalStateReqOutput {
rid: None,
http_worker_ipc: None,
updated: false,
server_args: ts_wire::Dict(Vec::new()),
},
));
}
ControlRequest::UpdateWeightsFromTensor(_) => {
self.reply(ControlOutput::UpdateWeightsFromTensor(
ts_wire::control::UpdateWeightsFromTensorReqOutput {
rid: None,
http_worker_ipc: None,
success: false,
message: "update_weights_from_tensor is not supported on this engine"
.to_owned(),
},
));
}
ControlRequest::Profile(_) => return Effect::Profile,
op @ (ControlRequest::InitWeightsUpdateGroup(_)
| ControlRequest::DestroyWeightsUpdateGroup(_)
| ControlRequest::RebalanceExperts(_)) => self.weight_ops.push_back(op),
op @ (ControlRequest::UpdateWeightsFromDistributed(_)
| ControlRequest::UpdateWeightsFromMooncake(_)
| ControlRequest::UpdateWeightFromDisk(_)) => match self
.refuse_weight_load()
.or_else(|| self.l3_version_refusal(&op))
{
Some(message) => {
let reply = gated_reply(&op, false, message);
self.reply(reply);
}
None => self.weight_ops.push_back(op),
},
}
Effect::None
}
fn pause(&mut self, mode: &str) {
if !matches!(mode, "abort" | "wait" | "keep") {
self.reply(pause_reply(false, &format!("invalid pause mode: {mode:?}")));
return;
}
if !self.released.is_empty() {
self.reply(pause_reply(
false,
"memory is unavailable; use resume_memory_occupation",
));
return;
}
if self.drain.is_some() {
self.reply(pause_reply(false, "a pause is already in progress"));
return;
}
if mode == "keep" {
self.state = PauseState::PausedAll;
self.reply(pause_reply(true, ""));
return;
}
self.request_drain(mode == "abort", Drain::Pause);
}
fn resume(&mut self) {
if !self.released.is_empty() {
self.reply(resume_reply(
false,
"memory is released; call resume_memory_occupation to wake before \
resuming the scheduler",
));
return;
}
match self.drain.take() {
Some(Drain::Pause) => self.reply(pause_reply(false, "resumed before pause drained")),
Some(Drain::Release(_)) => {
self.release_pending = false;
self.reply(release_reply(false, "resumed before release drained"));
}
None => {}
}
self.state = PauseState::Unpaused;
self.abort_all = false;
self.cancel_unscheduled = false;
self.reply(resume_reply(true, ""));
}
fn release(&mut self, tags: Option<&[String]>, refusal: Option<String>) {
let fail = |controls: &mut Self, message: String| {
controls.release_pending = false;
controls.reply(release_reply(false, &message));
};
if !self.config.memory_saver {
fail(
self,
"memory saver not enabled (--enable-memory-saver)".to_owned(),
);
return;
}
let tags = match self.normalize_tags(tags, &MEMORY_REGIONS) {
Ok(tags) => tags,
Err(error) => return fail(self, error),
};
let already: Vec<&String> = tags
.iter()
.filter(|tag| self.released.contains(tag))
.collect();
if !already.is_empty() {
let message = format!("tags already released: {already:?}");
return fail(self, message);
}
if self.drain.is_some() {
return fail(self, "a pause or release is already in progress".to_owned());
}
if self.state == PauseState::PausedAll && self.released.is_empty() {
return fail(
self,
"a keep pause froze the scheduler; resume_scheduler before releasing memory"
.to_owned(),
);
}
if let Some(refusal) = refusal {
return fail(self, format!("cannot release memory: {refusal}"));
}
self.release_pending = true;
self.request_drain(false, Drain::Release(tags));
}
fn resume_memory(&mut self, tags: Option<&[String]>) -> Effect {
let resume_order: Vec<&str> = MEMORY_REGIONS.iter().rev().copied().collect();
let tags = match tags {
None => resume_order
.iter()
.filter(|tag| self.released.iter().any(|r| r == *tag))
.map(|tag| (*tag).to_owned())
.collect(),
Some(tags) => match self.normalize_tags(Some(tags), &resume_order) {
Ok(tags) => tags,
Err(error) => {
self.reply(resume_memory_reply(false, &error));
return Effect::None;
}
},
};
let not_released: Vec<&String> = tags
.iter()
.filter(|tag| !self.released.contains(tag))
.collect();
if !not_released.is_empty() {
let message = format!("tags not released: {not_released:?}");
self.reply(resume_memory_reply(false, &message));
return Effect::None;
}
if tags.is_empty() {
self.reply(resume_memory_reply(true, ""));
return Effect::None;
}
Effect::ResumeMemory(tags)
}
}
#[cfg(test)]
mod tests {
use ts_wire::control::{
PauseSchedulerReqInput, ReleaseMemoryOccupationReqInput, ResumeSchedulerReqInput,
};
use super::*;
fn pause(mode: &str) -> ControlRequest {
ControlRequest::PauseScheduler(PauseSchedulerReqInput {
rid: None,
http_worker_ipc: None,
mode: mode.to_owned(),
})
}
fn resume() -> ControlRequest {
ControlRequest::ResumeScheduler(ResumeSchedulerReqInput {
rid: None,
http_worker_ipc: None,
})
}
fn release(tags: Option<Vec<&str>>) -> ControlRequest {
ControlRequest::ReleaseMemoryOccupation(ReleaseMemoryOccupationReqInput {
rid: None,
http_worker_ipc: None,
tags: tags.map(|tags| tags.into_iter().map(str::to_owned).collect()),
})
}
fn successes(replies: &[ControlOutput]) -> Vec<(&'static str, bool)> {
replies
.iter()
.map(|reply| {
let success = match reply {
ControlOutput::PauseScheduler(r) => r.success,
ControlOutput::ResumeScheduler(r) => r.success,
ControlOutput::ReleaseMemoryOccupation(r) => r.success,
ControlOutput::ResumeMemoryOccupation(r) => r.success,
ControlOutput::UpdateWeightFromDisk(r) => r.success,
ControlOutput::UpdateWeightsFromMooncake(r) => r.success,
_ => true,
};
(reply.tag(), success)
})
.collect()
}
fn controls(memory_saver: bool) -> Controls {
Controls::new(ControlConfig {
memory_saver,
memory_regions: vec!["kv_cache".to_owned(), "weights".to_owned()],
l3_weight_version: None,
})
}
#[test]
fn a_wait_pause_answers_after_its_drain_and_a_resume_cancels_it() {
let mut controls = controls(false);
controls.handle(pause("wait"), None);
assert!(controls.admit_blocked() && !controls.forward_blocked());
assert!(controls.take_replies().is_empty());
controls.handle(pause("keep"), None);
assert_eq!(
successes(&controls.take_replies()),
[("PauseSchedulerReqOutput", false)]
);
controls.handle(resume(), None);
assert_eq!(
successes(&controls.take_replies()),
[
("PauseSchedulerReqOutput", false),
("ResumeSchedulerReqOutput", true)
]
);
assert!(!controls.admit_blocked());
controls.handle(pause("abort"), None);
assert!(controls.take_abort_all());
assert!(!controls.take_abort_all());
assert!(controls.take_cancel_unscheduled());
assert!(!controls.take_cancel_unscheduled());
assert_eq!(controls.take_drain(), Some(Drain::Pause));
controls.finish_pause_drain();
assert_eq!(
successes(&controls.take_replies()),
[("PauseSchedulerReqOutput", true)]
);
}
fn mooncake_load(flush_cache: bool, weight_version: Option<&str>) -> ControlRequest {
ControlRequest::UpdateWeightsFromMooncake(
ts_wire::control::UpdateWeightsFromMooncakeReqInput {
rid: None,
http_worker_ipc: None,
version: 7,
flush_cache,
weight_version: weight_version.map(str::to_owned),
},
)
}
#[test]
fn l3_loads_keep_the_namespace_tied_to_the_checkpoint() {
let mut l3 = Controls::new(ControlConfig {
memory_saver: false,
memory_regions: Vec::new(),
l3_weight_version: Some("v1".to_owned()),
});
assert_eq!(
l3.commit_l3_weight_version(&mooncake_load(true, None)),
Ok(Some("7".to_owned()))
);
l3.handle(mooncake_load(false, Some("v2")), None);
assert_eq!(
successes(&l3.take_replies()),
[("UpdateWeightsFromMooncakeReqOutput", false)]
);
assert_eq!(
l3.commit_l3_weight_version(&mooncake_load(false, Some("7"))),
Ok(None)
);
assert_eq!(
l3.commit_l3_weight_version(&mooncake_load(false, None)),
Ok(None)
);
let mut plain = controls(false);
assert_eq!(
plain.commit_l3_weight_version(&mooncake_load(false, Some("x"))),
Ok(None)
);
}
#[test]
fn a_release_neither_ends_a_keep_pause_nor_thaws_a_released_engine() {
let mut controls = controls(true);
controls.handle(pause("keep"), None);
controls.take_replies();
controls.handle(release(None), None);
assert_eq!(
successes(&controls.take_replies()),
[("ReleaseMemoryOccupationReqOutput", false)]
);
assert!(controls.forward_blocked() && controls.drain().is_none());
controls.handle(resume(), None);
controls.take_replies();
controls.handle(release(Some(vec!["kv_cache"])), None);
let Some(Drain::Release(tags)) = controls.take_drain() else {
panic!("a release drains");
};
controls.finish_release(tags);
controls.take_replies();
controls.handle(release(Some(vec!["weights"])), None);
assert!(controls.forward_blocked());
assert_eq!(
controls.drain(),
Some(&Drain::Release(vec!["weights".to_owned()]))
);
}
#[test]
fn a_keep_pause_freezes_at_once() {
let mut controls = controls(false);
controls.handle(pause("keep"), None);
assert!(controls.forward_blocked());
assert_eq!(
successes(&controls.take_replies()),
[("PauseSchedulerReqOutput", true)]
);
controls.handle(pause("bogus"), None);
assert_eq!(
successes(&controls.take_replies()),
[("PauseSchedulerReqOutput", false)]
);
}
#[test]
fn a_release_drains_frees_and_blocks_weight_loads_until_resumed() {
let mut off = controls(false);
off.handle(release(None), None);
assert_eq!(
successes(&off.take_replies()),
[("ReleaseMemoryOccupationReqOutput", false)]
);
let mut controls = controls(true);
controls.handle(release(Some(vec!["nccl"])), None);
assert_eq!(
successes(&controls.take_replies()),
[("ReleaseMemoryOccupationReqOutput", false)]
);
controls.handle(release(None), Some("buffered PD requests".to_owned()));
assert_eq!(
successes(&controls.take_replies()),
[("ReleaseMemoryOccupationReqOutput", false)]
);
controls.handle(release(None), None);
assert!(controls.releasing_or_released());
let Some(Drain::Release(tags)) = controls.take_drain() else {
panic!("a release drains");
};
assert_eq!(tags, ["kv_cache", "weights"]);
controls.finish_release(tags);
assert!(controls.forward_blocked() && controls.memory_released());
let load =
ControlRequest::UpdateWeightFromDisk(ts_wire::control::UpdateWeightFromDiskReqInput {
rid: None,
http_worker_ipc: None,
model_path: "/ckpt".to_owned(),
load_format: None,
weight_version: None,
flush_cache: true,
});
controls.take_replies();
controls.handle(load, None);
assert!(!controls.weights_busy());
assert_eq!(
successes(&controls.take_replies()),
[("UpdateWeightFromDiskReqOutput", false)]
);
let resume_memory = ControlRequest::ResumeMemoryOccupation(
ts_wire::control::ResumeMemoryOccupationReqInput {
rid: None,
http_worker_ipc: None,
tags: None,
},
);
let Effect::ResumeMemory(tags) = controls.handle(resume_memory, None) else {
panic!("a resume wakes the released regions");
};
assert_eq!(tags, ["weights", "kv_cache"]);
controls.finish_resume(&tags);
assert!(!controls.admit_blocked() && !controls.memory_released());
}
}