use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use crate::participant::api::Participant;
use crate::participant::bus_log;
use crate::participant::clock::ClockMode;
use crate::participant::clock::ClockSource;
use crate::participant::clock::real::RealClock;
use crate::participant::launch::Launch;
use crate::participant::runner::harness::TestHarness;
pub(crate) mod event_loop;
#[allow(
dead_code,
reason = "compiled in every profile because a domain module never asks which profile it is in; its only consumer is a module one profile declares"
)]
pub(crate) mod harness;
pub(crate) mod inputs;
pub(crate) mod lifecycle;
pub(crate) mod query;
pub(crate) mod signal;
pub(crate) mod startup;
pub(crate) mod teardown;
#[cfg(test)]
mod tests;
use lifecycle::BusLease;
use signal::shutdown_signal;
use startup::PreparedRun;
pub(crate) struct ShutdownRequest {
requested: AtomicBool,
notify: tokio::sync::Notify,
}
impl ShutdownRequest {
fn new() -> Self {
Self {
requested: AtomicBool::new(false),
notify: tokio::sync::Notify::new(),
}
}
fn trigger(&self) {
if !self.requested.swap(true, Ordering::Release) {
self.notify.notify_waiters();
}
}
fn is_requested(&self) -> bool {
self.requested.load(Ordering::Acquire)
}
async fn wait(&self) {
if self.is_requested() {
return;
}
let notified = self.notify.notified();
if self.is_requested() {
return;
}
notified.await;
}
}
pub(crate) struct ShutdownController<S> {
request: Arc<ShutdownRequest>,
source: Pin<Box<S>>,
}
impl<S> ShutdownController<S>
where
S: Future<Output = ()>,
{
pub(crate) fn new(source: S) -> Self {
Self {
request: Arc::new(ShutdownRequest::new()),
source: Box::pin(source),
}
}
pub(crate) fn is_requested(&self) -> bool {
self.request.is_requested()
}
pub(crate) async fn wait(&mut self) {
if self.request.is_requested() {
return;
}
tokio::select! {
biased;
_ = self.request.wait() => {},
_ = &mut self.source => self.request.trigger(),
}
}
}
pub fn run<R: Participant>() -> crate::Result<()> {
let tokio_runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()?;
tokio_runtime.block_on(run_async::<R>())
}
pub async fn run_async<R: Participant>() -> crate::Result<()> {
R::__retain_embedded_metadata();
bus_log::init_tracing();
let launch = Launch::parse()?;
let shutdown = shutdown_signal()?;
startup::run_supervised::<R, _>(launch, shutdown).await
}
#[allow(
dead_code,
reason = "compiled in every profile because a domain module never asks which profile it is in; its only consumer is a module one profile declares"
)]
pub async fn run_test_harness<R, S>(
bus: &crate::bus::BusHandle,
harness: TestHarness,
shutdown: S,
) -> crate::Result<()>
where
R: Participant,
S: Future<Output = ()>,
{
bus_log::init_tracing();
let query_reply_delay = harness.query_reply_delay;
let clock = RealClock::new(harness.timeline);
let config = inputs::deserialize_config::<R::Config>(harness.config.as_ref())?;
startup::validate_clock_inputs::<R, _>(ClockMode::Real, Some(&clock))?;
let mut shutdown = ShutdownController::new(shutdown);
lifecycle::run(
PreparedRun::<R, RealClock> {
bus: bus.clone(),
session: BusLease::Borrowed,
participant_id: harness.participant_id,
shutdown_grace: harness.shutdown_grace,
bundle: None,
config,
clock_mode: ClockMode::Real,
clock: Some(clock),
query_reply_delay,
},
&mut shutdown,
)
.await
}
#[doc(hidden)]
#[allow(
dead_code,
reason = "compiled in every profile because a domain module never asks which profile it is in; its only consumer is a module one profile declares"
)]
pub async fn run_test_harness_with_clock<R, C, S>(
bus: &crate::bus::BusHandle,
harness: TestHarness,
clock: C,
shutdown: S,
) -> crate::Result<()>
where
R: Participant + crate::__private::surface::TypedIoSurface,
C: ClockSource,
S: Future<Output = ()>,
{
bus_log::init_tracing();
let query_reply_delay = harness.query_reply_delay;
let config = inputs::deserialize_config::<R::Config>(harness.config.as_ref())?;
startup::validate_clock_inputs::<R, _>(ClockMode::Real, Some(&clock))?;
let mut shutdown = ShutdownController::new(shutdown);
lifecycle::run(
PreparedRun::<R, C> {
bus: bus.clone(),
session: BusLease::Borrowed,
participant_id: harness.participant_id,
shutdown_grace: harness.shutdown_grace,
bundle: None,
config,
clock_mode: ClockMode::Real,
clock: Some(clock),
query_reply_delay,
},
&mut shutdown,
)
.await
}