1use anyhow::{Context, ensure};
7use tokio::sync::mpsc;
8use tokio::task::JoinHandle;
9use tokio_util::sync::CancellationToken;
10
11use crate::common::protocols::{FpmPublisher, KvEventPublishers, MockEngineArgs, OutputSignal};
12use crate::grouped_scheduler::{
13 GroupedSchedulerRankEventSinks, GroupedSchedulers,
14 create_single_rank_scheduler_with_event_sender,
15};
16use crate::scheduler::{SchedulerEventSender, SchedulerHandle};
17
18pub(crate) struct LiveEngineScheduler {
19 pub(crate) handle: Box<dyn SchedulerHandle>,
20 pub(crate) actor: JoinHandle<anyhow::Result<()>>,
21 pub(crate) completion_drain: crate::grouped_scheduler::CompletionBoundaryDrain,
22}
23
24pub fn create_engine(
29 args: MockEngineArgs,
30 dp_rank: u32,
31 output_tx: Option<mpsc::UnboundedSender<Vec<OutputSignal>>>,
32 kv_event_publishers: KvEventPublishers,
33 cancellation_token: Option<CancellationToken>,
34 fpm_publisher: FpmPublisher,
35) -> anyhow::Result<Box<dyn SchedulerHandle>> {
36 let LiveEngineScheduler {
37 handle,
38 actor,
39 completion_drain: _,
40 } = create_engine_with_event_sender(
41 args,
42 dp_rank,
43 output_tx.map(SchedulerEventSender::from),
44 kv_event_publishers,
45 cancellation_token,
46 fpm_publisher,
47 )?;
48 drop(actor);
51 Ok(handle)
52}
53
54pub(crate) fn create_engine_with_event_sender(
55 args: MockEngineArgs,
56 dp_rank: u32,
57 event_tx: Option<SchedulerEventSender>,
58 kv_event_publishers: KvEventPublishers,
59 cancellation_token: Option<CancellationToken>,
60 fpm_publisher: FpmPublisher,
61) -> anyhow::Result<LiveEngineScheduler> {
62 ensure!(
67 args.dp_size == 1,
68 "single-rank create_engine does not support attention DP; use create_grouped_scheduler"
69 );
70 ensure!(
71 dp_rank == 0,
72 "single-rank create_engine requires dp_rank=0; use create_grouped_scheduler"
73 );
74 let GroupedSchedulers {
75 mut schedulers,
76 actor,
77 completion_drain,
78 } = create_single_rank_scheduler_with_event_sender(
79 args,
80 dp_rank,
81 GroupedSchedulerRankEventSinks {
82 event_tx,
83 kv_event_publishers,
84 fpm_publisher,
85 },
86 cancellation_token,
87 )?;
88 let handle = schedulers
89 .pop()
90 .context("single-rank generalized Mocker engine returned no scheduler handle")?;
91 ensure!(
92 schedulers.is_empty(),
93 "single-rank generalized Mocker engine returned extra scheduler handles"
94 );
95 Ok(LiveEngineScheduler {
96 handle,
97 actor,
98 completion_drain,
99 })
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[tokio::test]
107 async fn compatibility_entrypoint_rejects_attention_dp() {
108 let args = MockEngineArgs {
109 dp_size: 4,
110 ..MockEngineArgs::default()
111 };
112 let cancel = CancellationToken::new();
113
114 let result = create_engine_with_event_sender(
115 args,
116 3,
117 None,
118 KvEventPublishers::default(),
119 Some(cancel.clone()),
120 FpmPublisher::default(),
121 );
122 let error = match result {
123 Ok(_) => panic!("attention DP must be rejected by the single-rank entrypoint"),
124 Err(error) => error,
125 };
126 assert!(error.to_string().contains("does not support attention DP"));
127 }
128}