use anyhow::{Context, ensure};
use tokio::sync::mpsc;
use tokio::task::JoinHandle;
use tokio_util::sync::CancellationToken;
use crate::common::protocols::{FpmPublisher, KvEventPublishers, MockerConfig, OutputSignal};
use crate::grouped_scheduler::{
GroupedSchedulers, RankOutputSink, RankSinks, create_single_rank_scheduler_with_rank_sink,
};
use crate::scheduler::SchedulerHandle;
pub(crate) struct LiveEngineScheduler {
pub(crate) handle: Box<dyn SchedulerHandle>,
pub(crate) actor: JoinHandle<anyhow::Result<()>>,
pub(crate) completion_drain: crate::grouped_scheduler::CompletionBoundaryDrain,
}
pub fn create_engine(
args: MockerConfig,
dp_rank: u32,
output_tx: Option<mpsc::UnboundedSender<Vec<OutputSignal>>>,
kv_event_publishers: KvEventPublishers,
cancellation_token: Option<CancellationToken>,
fpm_publisher: FpmPublisher,
) -> anyhow::Result<Box<dyn SchedulerHandle>> {
let LiveEngineScheduler {
handle,
actor,
completion_drain: _,
} = create_engine_with_rank_sink(
args,
dp_rank,
RankSinks {
output: output_tx.map(RankOutputSink::Channel).unwrap_or_default(),
kv_event_publishers,
fpm_publisher,
},
cancellation_token,
)?;
drop(actor);
Ok(handle)
}
pub(crate) fn create_engine_with_rank_sink(
args: MockerConfig,
dp_rank: u32,
rank_sink: RankSinks,
cancellation_token: Option<CancellationToken>,
) -> anyhow::Result<LiveEngineScheduler> {
ensure!(
args.dp_size == 1,
"single-rank create_engine does not support attention DP; use create_grouped_scheduler"
);
ensure!(
dp_rank == 0,
"single-rank create_engine requires dp_rank=0; use create_grouped_scheduler"
);
let GroupedSchedulers {
mut schedulers,
actor,
completion_drain,
} = create_single_rank_scheduler_with_rank_sink(args, dp_rank, rank_sink, cancellation_token)?;
let handle = schedulers
.pop()
.context("single-rank generalized Mocker engine returned no scheduler handle")?;
ensure!(
schedulers.is_empty(),
"single-rank generalized Mocker engine returned extra scheduler handles"
);
Ok(LiveEngineScheduler {
handle,
actor,
completion_drain,
})
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn compatibility_entrypoint_rejects_attention_dp() {
let args = MockerConfig::from_value(serde_json::json!({"dp_size":4})).unwrap();
let cancel = CancellationToken::new();
let result = create_engine(
args,
3,
None,
KvEventPublishers::default(),
Some(cancel.clone()),
FpmPublisher::default(),
);
let error = match result {
Ok(_) => panic!("attention DP must be rejected by the single-rank entrypoint"),
Err(error) => error,
};
assert!(error.to_string().contains("does not support attention DP"));
}
}