use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
use arc_swap::ArcSwap;
use crate::channel::ChannelSnapshot;
use crate::engine::FunctionRegistry;
use crate::model::ModelSet;
use crate::plugin::PluginSet;
pub struct RuntimeGeneration {
pub id: u64,
pub engine: Arc<dataflow_rs::Engine>,
pub channels: Arc<ChannelSnapshot>,
pub functions: Arc<FunctionRegistry>,
pub plugins: Arc<PluginSet>,
pub models: Arc<ModelSet>,
}
pub struct RuntimeHandle {
current: ArcSwap<RuntimeGeneration>,
published: AtomicU64,
}
impl RuntimeHandle {
pub fn new(
engine: Arc<dataflow_rs::Engine>,
channels: Arc<ChannelSnapshot>,
functions: Arc<FunctionRegistry>,
) -> Self {
Self {
current: ArcSwap::from_pointee(RuntimeGeneration {
id: 0,
engine,
channels,
functions,
plugins: Arc::new(PluginSet::empty()),
models: Arc::new(ModelSet::empty()),
}),
published: AtomicU64::new(0),
}
}
pub fn load(&self) -> Arc<RuntimeGeneration> {
self.current.load_full()
}
pub fn publish(
&self,
engine: Arc<dataflow_rs::Engine>,
channels: Arc<ChannelSnapshot>,
functions: Arc<FunctionRegistry>,
plugins: Arc<PluginSet>,
models: Arc<ModelSet>,
) -> u64 {
let id = self.published.fetch_add(1, Ordering::Relaxed) + 1;
self.current.store(Arc::new(RuntimeGeneration {
id,
engine,
channels,
functions,
plugins,
models,
}));
id
}
pub fn published_count(&self) -> u64 {
self.published.load(Ordering::Relaxed)
}
}
#[cfg(test)]
pub(crate) fn test_handle() -> Arc<RuntimeHandle> {
Arc::new(RuntimeHandle::new(
Arc::new(
dataflow_rs::Engine::builder()
.build()
.expect("an empty engine builds"),
),
Arc::new(ChannelSnapshot::empty()),
FunctionRegistry::builtin().clone(),
))
}
#[cfg(test)]
mod tests {
use super::*;
fn engine() -> Arc<dataflow_rs::Engine> {
Arc::new(
dataflow_rs::Engine::builder()
.build()
.expect("empty engine builds"),
)
}
fn handle() -> (RuntimeHandle, Arc<dataflow_rs::Engine>) {
let boot = engine();
(
RuntimeHandle::new(
boot.clone(),
Arc::new(ChannelSnapshot::empty()),
FunctionRegistry::builtin().clone(),
),
boot,
)
}
#[test]
fn a_held_generation_survives_a_publish_whole() {
let (handle, boot_engine) = handle();
let held = handle.load();
let next_engine = engine();
handle.publish(
next_engine.clone(),
Arc::new(ChannelSnapshot::empty()),
FunctionRegistry::builtin().clone(),
Arc::new(PluginSet::empty()),
Arc::new(ModelSet::empty()),
);
assert_eq!(held.id, 0);
assert!(
Arc::ptr_eq(&held.engine, &boot_engine),
"a held generation must keep the engine it was loaded with"
);
assert!(
!Arc::ptr_eq(&held.engine, &next_engine),
"and must not see the engine published after it"
);
assert_eq!(handle.load().id, 1, "a later load sees the new generation");
assert!(Arc::ptr_eq(&handle.load().engine, &next_engine));
}
#[test]
fn ids_count_publications() {
let (handle, _) = handle();
assert_eq!(handle.published_count(), 0);
assert_eq!(handle.load().id, 0);
for expected in 1..=3 {
let id = handle.publish(
engine(),
Arc::new(ChannelSnapshot::empty()),
FunctionRegistry::builtin().clone(),
Arc::new(PluginSet::empty()),
Arc::new(ModelSet::empty()),
);
assert_eq!(id, expected);
assert_eq!(handle.load().id, expected);
assert_eq!(handle.published_count(), expected);
}
}
}