use std::num::NonZeroUsize;
use observed::Sink;
use thread_aware::ThreadBuilder;
use tick::runtime::InactiveClock;
use crate::runtime::bootstrap;
use crate::runtime::config::{BlockingPoolPolicy, RuntimeConfig, WorkersPolicy};
use crate::runtime::error::Error;
use crate::runtime::handle::Runtime;
#[derive(Debug)]
pub struct RuntimeBuilder {
processor_config: RuntimeConfig,
clock: InactiveClock,
sink: Sink,
}
impl RuntimeBuilder {
#[must_use]
pub const fn workers(mut self, count: WorkersPolicy) -> Self {
self.processor_config.workers_policy = count;
self
}
#[must_use]
pub const fn stack_size(mut self, size: usize) -> Self {
assert!(size > 0, "stack size must be greater than zero");
self.processor_config.stack_size = NonZeroUsize::new(size).expect("size was asserted above to be nonzero");
self
}
#[must_use]
pub const fn blocking_pool(mut self, policy: BlockingPoolPolicy) -> Self {
self.processor_config.blocking_pool_policy = policy;
self
}
#[must_use]
pub fn clock(mut self, clock: impl Into<InactiveClock>) -> Self {
self.clock = clock.into();
self
}
#[must_use]
pub fn sink(mut self, sink: Sink) -> Self {
self.sink = sink;
self
}
pub fn build(self) -> Result<Runtime, Error> {
bootstrap::build(self.processor_config, &self.clock, self.sink, &ThreadBuilder::default())
}
}
impl RuntimeBuilder {
#[must_use]
pub(crate) fn new() -> Self {
Self {
clock: InactiveClock::default(),
processor_config: RuntimeConfig::default(),
sink: Sink::noop(),
}
}
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))] mod tests {
use std::time::Duration;
use tick::ClockControl;
use super::*;
#[test]
fn custom_clock_ok() {
let clock_control = ClockControl::new();
let builder = Runtime::builder().clock(clock_control.clone());
let (clock, _) = builder.clock.activate(std::task::Waker::noop().clone());
let now = clock.system_time();
clock_control.advance(Duration::from_secs(1));
assert_eq!(clock.system_time().duration_since(now).unwrap(), Duration::from_secs(1));
}
#[test]
fn resource_limits_preserve_independent_settings() {
let builder = Runtime::builder()
.workers(WorkersPolicy::exactly(2))
.stack_size(1024 * 1024)
.blocking_pool(BlockingPoolPolicy::shared().max(1));
assert_eq!(
builder.processor_config,
RuntimeConfig {
workers_policy: WorkersPolicy::exactly(2),
stack_size: NonZeroUsize::new(1024 * 1024).unwrap(),
blocking_pool_policy: BlockingPoolPolicy::shared().max(1),
}
);
}
#[test]
fn maximum_worker_count_replaces_exact_count() {
let builder = Runtime::builder()
.workers(WorkersPolicy::exactly(2))
.workers(WorkersPolicy::at_most(1));
assert_eq!(builder.processor_config.workers_policy, WorkersPolicy::at_most(1),);
}
#[test]
fn zero_worker_policies_can_be_replaced_before_building() {
for policy in [WorkersPolicy::exactly(0), WorkersPolicy::at_most(0)] {
let builder = Runtime::builder().workers(policy);
assert_eq!(builder.processor_config.workers_policy, policy);
let builder = builder.workers(WorkersPolicy::at_most(1));
assert_eq!(builder.processor_config.workers_policy, WorkersPolicy::at_most(1));
}
}
#[test]
fn all_workers_policy_replaces_maximum_count() {
let builder = Runtime::builder().workers(WorkersPolicy::at_most(1)).workers(WorkersPolicy::all());
assert_eq!(builder.processor_config.workers_policy, WorkersPolicy::all(),);
}
#[test]
fn automatic_worker_selection_replaces_exact_count() {
let builder = Runtime::builder()
.workers(WorkersPolicy::exactly(2))
.workers(WorkersPolicy::default());
assert_eq!(builder.processor_config.workers_policy, WorkersPolicy::default());
}
#[test]
fn exact_processor_selection_preserves_other_resource_settings() {
let builder = Runtime::builder()
.stack_size(1024 * 1024)
.blocking_pool(BlockingPoolPolicy::shared().max(1))
.workers(WorkersPolicy::at_most(1))
.workers(WorkersPolicy::exactly(2));
assert_eq!(
builder.processor_config,
RuntimeConfig {
workers_policy: WorkersPolicy::exactly(2),
stack_size: NonZeroUsize::new(1024 * 1024).unwrap(),
blocking_pool_policy: BlockingPoolPolicy::shared().max(1),
}
);
}
#[test]
fn build_and_stop_inside_futures_executor() {
futures::executor::block_on(async {
let runtime = Runtime::builder().workers(WorkersPolicy::exactly(1)).build().unwrap();
let (value, scheduler) = runtime
.scheduler()
.spawn_anywhere((), |cx, ()| async move { (42, cx.scheduler().clone()) })
.await
.unwrap();
assert_eq!(value, 42);
runtime.stop().unwrap();
assert!(scheduler.spawn(async |_| ()).await.unwrap_err().is_shutdown());
});
}
}