use std::sync::Arc;
use std::time::Duration;
use a3s_box_core::error::{BoxError, Result};
use async_trait::async_trait;
use crate::oci::build::cache::RecordedBuildCache;
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
const CANCELLATION_POLL_INTERVAL: Duration = Duration::from_millis(25);
#[async_trait]
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
pub(in crate::oci::build) trait BuildExecutionObserver: Send + Sync {
async fn cancellation_requested(&self) -> Result<bool>;
async fn acquire_image_commit_permit(&self) -> Result<BuildImageCommitPermit>;
async fn publish_cache_export(
&self,
_staged: RecordedBuildCache,
) -> Result<RecordedBuildCache> {
Err(BoxError::BuildError(
"recorded build cache export has no journal publisher".to_string(),
))
}
async fn run_process_started(&self, pid: u32, start_time: Option<u64>) -> Result<()>;
async fn run_process_finished(&self, pid: u32, start_time: Option<u64>) -> Result<()>;
}
trait SendGuard: Send {}
impl<T: Send> SendGuard for T {}
pub(in crate::oci::build) struct BuildImageCommitPermit {
_guard: Box<dyn SendGuard>,
}
impl BuildImageCommitPermit {
pub(in crate::oci::build) fn new(guard: impl Send + 'static) -> Self {
Self {
_guard: Box::new(guard),
}
}
}
#[derive(Clone)]
pub(in crate::oci::build) struct BuildExecutionControl {
observer: Arc<dyn BuildExecutionObserver>,
}
impl BuildExecutionControl {
pub(in crate::oci::build) fn new(observer: Arc<dyn BuildExecutionObserver>) -> Self {
Self { observer }
}
pub(in crate::oci::build) async fn ensure_active(&self) -> Result<()> {
if self.observer.cancellation_requested().await? {
return Err(cancelled_error());
}
Ok(())
}
pub(super) async fn acquire_image_commit_permit(&self) -> Result<BuildImageCommitPermit> {
self.observer.acquire_image_commit_permit().await
}
pub(super) async fn publish_cache_export(
&self,
staged: RecordedBuildCache,
) -> Result<RecordedBuildCache> {
self.observer.publish_cache_export(staged).await
}
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
pub(super) async fn wait_for_cancellation(&self) -> Result<()> {
loop {
if self.observer.cancellation_requested().await? {
return Ok(());
}
tokio::time::sleep(CANCELLATION_POLL_INTERVAL).await;
}
}
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
pub(super) async fn run_process_started(
&self,
pid: u32,
start_time: Option<u64>,
) -> Result<()> {
self.observer.run_process_started(pid, start_time).await
}
#[cfg_attr(not(target_os = "linux"), allow(dead_code))]
pub(super) async fn run_process_finished(
&self,
pid: u32,
start_time: Option<u64>,
) -> Result<()> {
self.observer.run_process_finished(pid, start_time).await
}
}
pub(super) fn cancelled_error() -> BoxError {
BoxError::BuildError("recorded build operation was cancelled".to_string())
}