1use std::sync::{Arc, Mutex, Weak};
5
6use anyhow::{anyhow, bail};
7use dashmap::DashMap;
8use dashmap::mapref::entry::Entry;
9use tokio::sync::mpsc;
10use tokio_util::sync::CancellationToken;
11use uuid::Uuid;
12
13use crate::common::handoff::{HandoffId, HandoffTransferTiming};
14use crate::scheduler::{SchedulerCommand, SchedulerCommandResult, SchedulerLifecycleEvent};
15
16use super::request::{Routes, shutdown_routes};
17use super::{
18 LiveEngine, LiveEngineInner, LiveRequestRegistration, PreparedSubmission, send_command,
19};
20
21const HANDOFF_EVENT_CAPACITY: usize = 8;
22
23#[derive(Default)]
24pub(super) struct HandoffRoutes {
25 by_id: DashMap<HandoffId, Arc<HandoffRoute>>,
26 last_by_id: DashMap<HandoffId, (Uuid, Weak<HandoffRoute>)>,
27}
28
29pub(super) type SharedHandoffRoutes = Arc<HandoffRoutes>;
30
31impl HandoffRoutes {
32 pub(super) fn is_empty(&self) -> bool {
33 self.by_id.is_empty()
34 }
35}
36
37struct HandoffRoute {
38 handoff_id: HandoffId,
39 generation: Uuid,
40 routes: Weak<HandoffRoutes>,
41 event_tx: Mutex<Option<mpsc::Sender<LiveHandoffEvent>>>,
42 command_lock: Arc<tokio::sync::Mutex<()>>,
43}
44
45impl HandoffRoute {
46 fn new(
47 handoff_id: HandoffId,
48 routes: Weak<HandoffRoutes>,
49 event_tx: mpsc::Sender<LiveHandoffEvent>,
50 ) -> Self {
51 Self {
52 handoff_id,
53 generation: Uuid::new_v4(),
54 routes,
55 event_tx: Mutex::new(Some(event_tx)),
56 command_lock: Arc::new(tokio::sync::Mutex::new(())),
57 }
58 }
59
60 async fn send(&self, event: LiveHandoffEvent, cancel: &CancellationToken) -> bool {
61 let event_tx = self.event_tx.lock().unwrap().as_ref().cloned();
62 let Some(event_tx) = event_tx else {
63 return false;
64 };
65 let delivered = tokio::select! {
66 biased;
67 _ = cancel.cancelled() => false,
68 result = event_tx.send(event) => result.is_ok(),
69 };
70 if !delivered {
71 self.shutdown();
72 }
73 delivered
74 }
75
76 fn shutdown(&self) {
77 self.event_tx.lock().unwrap().take();
78 }
79
80 fn is_latest_generation(&self, routes: &HandoffRoutes) -> bool {
81 match routes.last_by_id.get(&self.handoff_id) {
82 Some(current) => current.value().0 == self.generation,
83 None => false,
84 }
85 }
86}
87
88impl Drop for HandoffRoute {
89 fn drop(&mut self) {
90 let Some(routes) = self.routes.upgrade() else {
91 return;
92 };
93 routes
94 .last_by_id
95 .remove_if(&self.handoff_id, |_, (generation, _)| {
96 *generation == self.generation
97 });
98 }
99}
100
101#[derive(Clone, Copy, Debug, PartialEq)]
103pub enum LiveHandoffEvent {
104 SourceHeld {
105 transfer_timing: HandoffTransferTiming,
106 },
107 DestinationReserved {
108 transferable_prompt_tokens: usize,
109 },
110}
111
112impl LiveEngine {
113 pub fn register_handoff(
115 &self,
116 handoff_id: HandoffId,
117 ) -> anyhow::Result<(LiveHandoffControl, LiveHandoffEvents)> {
118 anyhow::ensure!(
119 !self.inner.cancel.is_cancelled(),
120 "live Mocker engine is not running"
121 );
122 let (event_tx, event_rx) = mpsc::channel(HANDOFF_EVENT_CAPACITY);
123 let previous_route = self
124 .inner
125 .handoff_routes
126 .last_by_id
127 .get(&handoff_id)
128 .and_then(|entry| entry.value().1.upgrade());
129 let _previous_command = match previous_route {
130 Some(route) => Some(route.command_lock.clone().try_lock_owned().map_err(|_| {
131 anyhow!("handoff {handoff_id:?} still has a scheduler command in progress")
132 })?),
133 None => None,
134 };
135 let route = Arc::new(HandoffRoute::new(
136 handoff_id,
137 Arc::downgrade(&self.inner.handoff_routes),
138 event_tx,
139 ));
140 match self.inner.handoff_routes.by_id.entry(handoff_id) {
141 Entry::Occupied(_) => {
142 bail!("handoff {handoff_id:?} already has a lifecycle route")
143 }
144 Entry::Vacant(entry) => {
145 entry.insert(Arc::clone(&route));
146 }
147 }
148 self.inner
149 .handoff_routes
150 .last_by_id
151 .insert(handoff_id, (route.generation, Arc::downgrade(&route)));
152 if self.inner.cancel.is_cancelled() {
153 route.shutdown();
154 remove_handoff_route(&self.inner.handoff_routes, &route);
155 bail!("live Mocker engine is not running");
156 }
157 Ok((
158 LiveHandoffControl {
159 engine: Arc::downgrade(&self.inner),
160 route: Arc::clone(&route),
161 },
162 LiveHandoffEvents {
163 route,
164 routes: Arc::clone(&self.inner.handoff_routes),
165 event_rx,
166 },
167 ))
168 }
169}
170
171#[derive(Clone)]
173pub struct LiveHandoffControl {
174 engine: Weak<LiveEngineInner>,
175 route: Arc<HandoffRoute>,
176}
177
178impl LiveHandoffControl {
179 pub fn handoff_id(&self) -> HandoffId {
180 self.route.handoff_id
181 }
182
183 pub async fn submit_prefill(
184 &self,
185 registration: LiveRequestRegistration,
186 ) -> anyhow::Result<()> {
187 let command_guard = self.route.command_lock.clone().lock_owned().await;
188 let engine = self.engine()?;
189 self.ensure_generation(&engine, false)?;
190 LiveEngine { inner: engine }
191 .submit_prepared(
192 registration,
193 PreparedSubmission::Source(self.route.handoff_id),
194 Some(command_guard),
195 )
196 .await
197 }
198
199 pub async fn reserve_destination(
200 &self,
201 registration: LiveRequestRegistration,
202 ) -> anyhow::Result<()> {
203 let command_guard = self.route.command_lock.clone().lock_owned().await;
204 let engine = self.engine()?;
205 self.ensure_generation(&engine, false)?;
206 LiveEngine { inner: engine }
207 .submit_prepared(
208 registration,
209 PreparedSubmission::Destination(DestinationCancellation {
210 route: Arc::clone(&self.route),
211 }),
212 Some(command_guard),
213 )
214 .await
215 }
216
217 pub async fn release_source(&self) -> anyhow::Result<()> {
218 self.send_handoff_command(
219 SchedulerCommand::ReleaseSource {
220 handoff_id: self.route.handoff_id,
221 },
222 HandoffCommandResult::AppliedOrNoop,
223 )
224 .await
225 }
226
227 pub async fn cancel_source(&self) -> anyhow::Result<()> {
228 self.send_handoff_command(
229 SchedulerCommand::CancelSource {
230 handoff_id: self.route.handoff_id,
231 },
232 HandoffCommandResult::AppliedOrNoop,
233 )
234 .await
235 }
236
237 pub async fn activate_destination(&self) -> anyhow::Result<()> {
238 self.send_handoff_command(
239 SchedulerCommand::ActivateDestination {
240 handoff_id: self.route.handoff_id,
241 },
242 HandoffCommandResult::Applied,
243 )
244 .await
245 }
246
247 pub async fn cancel_destination(&self) -> anyhow::Result<()> {
248 self.send_handoff_command(
249 SchedulerCommand::CancelDestination {
250 handoff_id: self.route.handoff_id,
251 },
252 HandoffCommandResult::AppliedOrNoop,
253 )
254 .await
255 }
256
257 async fn send_handoff_command(
258 &self,
259 command: SchedulerCommand,
260 expected: HandoffCommandResult,
261 ) -> anyhow::Result<()> {
262 let _command_guard = self.route.command_lock.clone().lock_owned().await;
263 let engine = self.engine()?;
264 self.ensure_generation(&engine, true)?;
265 anyhow::ensure!(
266 !engine.cancel.is_cancelled(),
267 "live Mocker engine is not running"
268 );
269 let result = send_command(&engine.command_tx, command).await?;
270 match (expected, result) {
271 (HandoffCommandResult::Applied, SchedulerCommandResult::Applied)
272 | (
273 HandoffCommandResult::AppliedOrNoop,
274 SchedulerCommandResult::Applied | SchedulerCommandResult::Noop,
275 ) => Ok(()),
276 (_, result) => Err(anyhow!(
277 "unexpected scheduler handoff result for {:?}: {result:?}",
278 self.route.handoff_id
279 )),
280 }
281 }
282
283 fn engine(&self) -> anyhow::Result<Arc<LiveEngineInner>> {
284 self.engine
285 .upgrade()
286 .ok_or_else(|| anyhow!("live Mocker engine no longer exists"))
287 }
288
289 fn ensure_generation(
290 &self,
291 engine: &LiveEngineInner,
292 allow_unregistered: bool,
293 ) -> anyhow::Result<()> {
294 match engine.handoff_routes.by_id.get(&self.route.handoff_id) {
295 Some(current) if Arc::ptr_eq(current.value(), &self.route) => Ok(()),
296 Some(_) => bail!(
297 "handoff {:?} control belongs to an earlier registration",
298 self.route.handoff_id
299 ),
300 None if allow_unregistered
301 && self.route.is_latest_generation(&engine.handoff_routes) =>
302 {
303 Ok(())
304 }
305 None if allow_unregistered => bail!(
306 "handoff {:?} control belongs to an earlier registration",
307 self.route.handoff_id
308 ),
309 None => bail!(
310 "handoff {:?} lifecycle route is no longer registered",
311 self.route.handoff_id
312 ),
313 }
314 }
315}
316
317#[derive(Clone)]
318pub(super) struct DestinationCancellation {
319 route: Arc<HandoffRoute>,
320}
321
322impl DestinationCancellation {
323 pub(super) fn handoff_id(&self) -> HandoffId {
324 self.route.handoff_id
325 }
326
327 pub(super) async fn cancel(
328 &self,
329 command_tx: &mpsc::Sender<crate::scheduler::SchedulerCommandEnvelope>,
330 ) -> anyhow::Result<bool> {
331 let _command_guard = self.route.command_lock.clone().lock_owned().await;
332 let Some(routes) = self.route.routes.upgrade() else {
333 return Ok(false);
334 };
335 if !self.route.is_latest_generation(&routes) {
336 return Ok(false);
337 }
338 super::cancel_destination(command_tx, self.route.handoff_id).await
339 }
340}
341
342#[derive(Clone, Copy)]
343enum HandoffCommandResult {
344 Applied,
345 AppliedOrNoop,
346}
347
348pub struct LiveHandoffEvents {
350 route: Arc<HandoffRoute>,
351 routes: SharedHandoffRoutes,
352 event_rx: mpsc::Receiver<LiveHandoffEvent>,
353}
354
355impl LiveHandoffEvents {
356 pub fn handoff_id(&self) -> HandoffId {
357 self.route.handoff_id
358 }
359
360 pub async fn recv(&mut self) -> Option<LiveHandoffEvent> {
361 self.event_rx.recv().await
362 }
363}
364
365impl Drop for LiveHandoffEvents {
366 fn drop(&mut self) {
367 self.route.shutdown();
368 remove_handoff_route(&self.routes, &self.route);
369 }
370}
371
372fn remove_handoff_route(routes: &HandoffRoutes, route: &Arc<HandoffRoute>) -> bool {
373 routes
374 .by_id
375 .remove_if(&route.handoff_id, |_, current| Arc::ptr_eq(current, route))
376 .is_some()
377}
378
379pub(super) async fn run_lifecycle_dispatcher(
380 mut lifecycle_rx: mpsc::Receiver<SchedulerLifecycleEvent>,
381 routes: SharedHandoffRoutes,
382 cancel: CancellationToken,
383) -> anyhow::Result<()> {
384 loop {
385 let event = tokio::select! {
386 biased;
387 _ = cancel.cancelled() => return Ok(()),
388 event = lifecycle_rx.recv() => {
389 let Some(event) = event else {
390 if cancel.is_cancelled() {
391 return Ok(());
392 }
393 bail!("live Mocker lifecycle lane closed unexpectedly");
394 };
395 event
396 }
397 };
398 let (handoff_id, event) = match event {
399 SchedulerLifecycleEvent::SourceHeld {
400 handoff_id,
401 transfer_timing,
402 ..
403 } => (handoff_id, LiveHandoffEvent::SourceHeld { transfer_timing }),
404 SchedulerLifecycleEvent::DestinationReserved {
405 handoff_id,
406 transferable_prompt_tokens,
407 ..
408 } => (
409 handoff_id,
410 LiveHandoffEvent::DestinationReserved {
411 transferable_prompt_tokens,
412 },
413 ),
414 };
415 let route = routes
416 .by_id
417 .get(&handoff_id)
418 .map(|entry| Arc::clone(entry.value()));
419 if let Some(route) = route
420 && !route.send(event, &cancel).await
421 {
422 remove_handoff_route(&routes, &route);
423 }
424 }
425}
426
427pub(super) async fn supervise_lifecycle_dispatcher(
428 dispatcher: tokio::task::JoinHandle<anyhow::Result<()>>,
429 routes: Routes,
430 handoff_routes: SharedHandoffRoutes,
431 cancel: CancellationToken,
432) -> anyhow::Result<()> {
433 let result = match dispatcher.await {
434 Ok(Ok(())) => Ok(()),
435 Ok(Err(error)) => Err(error),
436 Err(error) => Err(anyhow!(
437 "live Mocker lifecycle dispatcher task failed: {error}"
438 )),
439 };
440 if let Err(error) = &result {
441 tracing::error!(%error, "live Mocker lifecycle dispatcher failed");
442 } else if !cancel.is_cancelled() {
443 tracing::error!("live Mocker lifecycle dispatcher exited unexpectedly");
444 }
445 cancel.cancel();
446 shutdown_routes(&routes);
447 shutdown_handoff_routes(&handoff_routes);
448 result
449}
450
451pub(super) fn shutdown_handoff_routes(routes: &HandoffRoutes) {
452 let active_routes = routes
453 .by_id
454 .iter()
455 .map(|entry| Arc::clone(entry.value()))
456 .collect::<Vec<_>>();
457 for route in active_routes {
458 route.shutdown();
459 }
460 routes.by_id.clear();
461}