From e111f4a8038988b2fd9518fc54783829276971bf Mon Sep 17 00:00:00 2001 From: Billy Price Date: Fri, 20 Feb 2026 09:53:44 -0800 Subject: [PATCH] Add support for uniform spawning of services using the resources + inner-service pattern --- embedded-service/src/lib.rs | 1 + embedded-service/src/service.rs | 92 ++++++++++ espi-service/src/espi_service.rs | 76 +++++++-- espi-service/src/lib.rs | 3 - espi-service/src/task.rs | 7 - examples/rt685s-evk/src/bin/time_alarm.rs | 34 ++-- time-alarm-service/src/lib.rs | 197 ++++++++++++++++------ time-alarm-service/src/task.rs | 9 - time-alarm-service/tests/tad_test.rs | 47 +++--- 9 files changed, 336 insertions(+), 130 deletions(-) create mode 100644 embedded-service/src/service.rs delete mode 100644 espi-service/src/task.rs delete mode 100644 time-alarm-service/src/task.rs diff --git a/embedded-service/src/lib.rs b/embedded-service/src/lib.rs index c53aba53..2db63f7c 100644 --- a/embedded-service/src/lib.rs +++ b/embedded-service/src/lib.rs @@ -22,6 +22,7 @@ pub mod init; pub mod ipc; pub mod keyboard; pub mod relay; +pub mod service; pub mod sync; /// Hidden re-exports used by macros defined in this crate. diff --git a/embedded-service/src/service.rs b/embedded-service/src/service.rs new file mode 100644 index 00000000..8b6c75fc --- /dev/null +++ b/embedded-service/src/service.rs @@ -0,0 +1,92 @@ +//! This module contains helper traits and functions for services that run on the EC. + +/// A trait for a service that can be run on the EC. +/// Implementations of Service should have an init() function to construct the service that +/// returns a Runner, which the user is expected to spawn a task for. +pub trait Service<'hw>: Sized { + /// A type that can be used to run the service. This is returned by the init() function and the user is + /// expected to call its run() method in an embassy task (or similar parallel execution context on other + /// async runtimes). + type Runner: ServiceRunner<'hw>; + + /// Any memory resources that your service needs. This is typically an opaque types that is only used by the service + /// and is not interacted with by users of the service. Must be default-constructible for spawn_service!() to work. + type Resources: ServiceResources; + + /// The error type that your `init` function can return on failure. + type ErrorType; + + /// Any initialization parameters that your service needs to run. + type InitParams; + + /// Initializes an instance of the service using the provided storage and returns a control handle for the service and + /// a runner that can be used to run the service. + fn new( + storage: &'hw mut Self::Resources, + params: Self::InitParams, + ) -> impl core::future::Future>; +} + +/// Represents the memory resources that a service needs to run. This is typically an opaque type that is only used by the service. +/// Must be default-constructible; the service is expected to populate its contents in its init() function. +pub trait ServiceResources { + /// Creates a new instance of the service's resources. + fn new() -> Self; +} + +/// A trait for a run handle used to execute a service's event loop. This is returned by Service::init() +/// and the user is expected to call its run() method in an embassy task (or similar parallel execution context +/// on other async runtimes). +pub trait ServiceRunner<'hw> { + /// Run the service event loop. This future never completes. + fn run(self) -> impl core::future::Future + 'hw; +} + +/// Initializes a service, creates an embassy task to run it, and spawns that task. +/// +/// This macro handles the boilerplate of: +/// 1. Creating a `static` [`OnceLock`](embassy_sync::once_lock::OnceLock) to hold the service +/// 2. Calling the service's `init()` method +/// 3. Defining an embassy_executor::task to run the service +/// 4. Spawning the task on the provided executor +/// +/// Returns a Result where Error is the error type of $service_ty::init(). +/// +/// Arguments +/// +/// - spawner: An embassy_executor::Spawner. +/// - service_ty: The service type that implements Service that you want to create and run. +/// - init_arg: The init argument type to pass to `Service::init()` +/// +/// Example: +/// +/// ```ignore +/// let time_service = embedded_services::spawn_service!( +/// spawner, +/// time_alarm_service::Service<'static>, +/// time_alarm_service::ServiceInitParams { dt_clock, tz, ac_expiration, ac_policy, dc_expiration, dc_policy } +/// ).expect("failed to initialize time_alarm service"); +/// ``` +#[macro_export] +macro_rules! spawn_service { + ($spawner:expr, $service_ty:ty, $init_arg:expr) => {{ + use $crate::service::{Service, ServiceResources, ServiceRunner}; + static SERVICE_RESOURCES: StaticCell<(<$service_ty as Service>::Resources)> = StaticCell::new(); + let service_resources = + SERVICE_RESOURCES.init(<<$service_ty as Service>::Resources as ServiceResources>::new()); + + #[embassy_executor::task] + async fn service_task_fn(runner: <$service_ty as $crate::service::Service<'static>>::Runner) { + runner.run().await; + } + + <$service_ty>::new(service_resources, $init_arg) + .await + .map(|(control_handle, runner)| { + $spawner.must_spawn(service_task_fn(runner)); + control_handle + }) + }}; +} + +pub use spawn_service; diff --git a/espi-service/src/espi_service.rs b/espi-service/src/espi_service.rs index 0a7c1d93..2dc4ba03 100644 --- a/espi-service/src/espi_service.rs +++ b/espi-service/src/espi_service.rs @@ -4,7 +4,6 @@ use embassy_futures::select::select; use embassy_imxrt::espi; use embassy_sync::channel::Channel; use embassy_sync::mutex::Mutex; -use embassy_sync::once_lock::OnceLock; use embedded_services::{GlobalRawMutex, error, info, trace}; use mctp_rs::smbus_espi::SmbusEspiMedium; use mctp_rs::smbus_espi::SmbusEspiReplyContext; @@ -32,28 +31,77 @@ pub enum Error { Buffer(embedded_services::buffer::Error), } +/// The memory required by the eSPI service to run +pub struct Resources<'hw, RelayHandler: embedded_services::relay::mctp::RelayHandler> { + inner: Option>, +} + +impl<'hw, RelayHandler: embedded_services::relay::mctp::RelayHandler> embedded_services::service::ServiceResources + for Resources<'hw, RelayHandler> +{ + fn new() -> Self { + Self { inner: None } + } +} + +/// Service runner for the eSPI service. Users must call the run() method on the service to start processing events. +pub struct Runner<'hw, RelayHandler: embedded_services::relay::mctp::RelayHandler> { + inner: &'hw ServiceInner<'hw, RelayHandler>, +} + +impl<'hw, RelayHandler: embedded_services::relay::mctp::RelayHandler> embedded_services::service::ServiceRunner<'hw> + for Runner<'hw, RelayHandler> +{ + /// Run the service event loop. + async fn run(self) -> embedded_services::Never { + self.inner.run().await + } +} + pub struct Service<'hw, RelayHandler: embedded_services::relay::mctp::RelayHandler> { + _inner: &'hw ServiceInner<'hw, RelayHandler>, +} + +impl<'hw, RelayHandler: embedded_services::relay::mctp::RelayHandler> embedded_services::service::Service<'hw> + for Service<'hw, RelayHandler> +{ + type Resources = Resources<'hw, RelayHandler>; + type Runner = Runner<'hw, RelayHandler>; + type ErrorType = core::convert::Infallible; + type InitParams = InitParams<'hw, RelayHandler>; + + async fn new( + resources: &'hw mut Self::Resources, + params: InitParams<'hw, RelayHandler>, + ) -> Result<(Self, Self::Runner), core::convert::Infallible> { + let inner = resources.inner.insert(ServiceInner::new(params).await); + Ok((Self { _inner: inner }, Runner { inner })) + } +} + +pub struct InitParams<'hw, RelayHandler: embedded_services::relay::mctp::RelayHandler> { + pub espi: espi::Espi<'hw>, + pub relay_handler: RelayHandler, +} + +struct ServiceInner<'hw, RelayHandler: embedded_services::relay::mctp::RelayHandler> { espi: Mutex>, host_tx_queue: Channel, HOST_TX_QUEUE_SIZE>, relay_handler: RelayHandler, } -impl<'hw, RelayHandler: embedded_services::relay::mctp::RelayHandler> Service<'hw, RelayHandler> { - pub async fn init( - service_storage: &'hw OnceLock, - mut espi: espi::Espi<'hw>, - relay_handler: RelayHandler, - ) -> &'hw Self { - espi.wait_for_plat_reset().await; +impl<'hw, RelayHandler: embedded_services::relay::mctp::RelayHandler> ServiceInner<'hw, RelayHandler> { + async fn new(mut init_params: InitParams<'hw, RelayHandler>) -> Self { + init_params.espi.wait_for_plat_reset().await; - service_storage.get_or_init(|| Service { - espi: Mutex::new(espi), + Self { + espi: Mutex::new(init_params.espi), host_tx_queue: Channel::new(), - relay_handler, - }) + relay_handler: init_params.relay_handler, + } } - pub(crate) async fn run_service(&self) -> ! { + async fn run(&self) -> embedded_services::Never { let mut espi = self.espi.lock().await; loop { let event = select(espi.wait_for_event(), self.host_tx_queue.receive()).await; @@ -180,9 +228,7 @@ impl<'hw, RelayHandler: embedded_services::relay::mctp::RelayHandler> Service<'h } Ok(()) } -} -impl<'hw, RelayHandler: embedded_services::relay::mctp::RelayHandler> Service<'hw, RelayHandler> { async fn process_request_to_ec( &self, (header, body): ( diff --git a/espi-service/src/lib.rs b/espi-service/src/lib.rs index c3f65e52..ce0c51fd 100644 --- a/espi-service/src/lib.rs +++ b/espi-service/src/lib.rs @@ -22,8 +22,5 @@ #[cfg(not(test))] mod espi_service; -#[cfg(not(test))] -pub mod task; - #[cfg(not(test))] pub use espi_service::*; diff --git a/espi-service/src/task.rs b/espi-service/src/task.rs deleted file mode 100644 index 578551c5..00000000 --- a/espi-service/src/task.rs +++ /dev/null @@ -1,7 +0,0 @@ -use crate::Service; - -pub async fn espi_service<'hw, R: embedded_services::relay::mctp::RelayHandler>( - espi_service: &'hw Service<'hw, R>, -) -> Result { - espi_service.run_service().await -} diff --git a/examples/rt685s-evk/src/bin/time_alarm.rs b/examples/rt685s-evk/src/bin/time_alarm.rs index 9b1a8182..be035eb5 100644 --- a/examples/rt685s-evk/src/bin/time_alarm.rs +++ b/examples/rt685s-evk/src/bin/time_alarm.rs @@ -1,7 +1,6 @@ #![no_std] #![no_main] -use embassy_sync::once_lock::OnceLock; use embedded_mcu_hal::{ Nvram, time::{Datetime, Month, UncheckedDatetime}, @@ -24,25 +23,19 @@ async fn main(spawner: embassy_executor::Spawner) { embedded_services::init().await; info!("services initialized"); - static TIME_SERVICE: OnceLock = OnceLock::new(); - let time_service = time_alarm_service::Service::init( - &TIME_SERVICE, - dt_clock, - tz, - ac_expiration, - ac_policy, - dc_expiration, - dc_policy, + let time_service = embedded_services::spawn_service!( + spawner, + time_alarm_service::Service<'static>, + time_alarm_service::InitParams { + backing_clock: dt_clock, + tz_storage: tz, + ac_expiration_storage: ac_expiration, + ac_policy_storage: ac_policy, + dc_expiration_storage: dc_expiration, + dc_policy_storage: dc_policy + } ) - .await - .expect("Failed to initialize time-alarm service"); - - #[embassy_executor::task] - async fn time_alarm_task(service: &'static time_alarm_service::Service<'static>) { - time_alarm_service::task::run_service(service).await - } - - spawner.must_spawn(time_alarm_task(time_service)); + .expect("Failed to spawn time alarm service"); use embedded_services::relay::mctp::impl_odp_mctp_relay_handler; impl_odp_mctp_relay_handler!( @@ -50,12 +43,11 @@ async fn main(spawner: embassy_executor::Spawner) { TimeAlarm, 0x0B, time_alarm_service::Service<'static>; ); - let _relay_handler = EspiRelayHandler::new(time_service); + let _relay_handler = EspiRelayHandler::new(&time_service); // Here, you'd normally pass _relay_handler to your relay service (e.g. eSPI service). // In this example, we're not leveraging a relay service, so we'll just demonstrate some direct calls. // - time_service .set_real_time(AcpiTimestamp { datetime: Datetime::new(UncheckedDatetime { diff --git a/time-alarm-service/src/lib.rs b/time-alarm-service/src/lib.rs index 333420ae..f0125b3e 100644 --- a/time-alarm-service/src/lib.rs +++ b/time-alarm-service/src/lib.rs @@ -2,7 +2,6 @@ use core::cell::RefCell; use embassy_sync::blocking_mutex::Mutex; -use embassy_sync::once_lock::OnceLock; use embassy_sync::signal::Signal; use embedded_mcu_hal::NvramStorage; use embedded_mcu_hal::time::{Datetime, DatetimeClock, DatetimeClockError}; @@ -10,7 +9,6 @@ use embedded_services::GlobalRawMutex; use embedded_services::{info, warn}; use time_alarm_service_messages::*; -pub mod task; mod timer; use timer::Timer; @@ -104,7 +102,19 @@ impl<'hw> Timers<'hw> { // ------------------------------------------------- -pub struct Service<'hw> { +/// Parameters required to initialize the time/alarm service. +pub struct InitParams<'hw> { + pub backing_clock: &'hw mut dyn DatetimeClock, + pub tz_storage: &'hw mut dyn NvramStorage<'hw, u32>, + pub ac_expiration_storage: &'hw mut dyn NvramStorage<'hw, u32>, + pub ac_policy_storage: &'hw mut dyn NvramStorage<'hw, u32>, + pub dc_expiration_storage: &'hw mut dyn NvramStorage<'hw, u32>, + pub dc_policy_storage: &'hw mut dyn NvramStorage<'hw, u32>, +} + +/// The main service implementation. Users will interact with this via the Service struct, which is a thin wrapper around this that allows +/// the client to provide storage for the service. +struct ServiceInner<'hw> { clock_state: Mutex>>, // TODO [POWER_SOURCE] signal this whenever the power source changes @@ -115,29 +125,19 @@ pub struct Service<'hw> { capabilities: TimeAlarmDeviceCapabilities, } -impl<'hw> Service<'hw> { - pub async fn init( - service_storage: &'hw OnceLock>, - backing_clock: &'hw mut impl DatetimeClock, - tz_storage: &'hw mut dyn NvramStorage<'hw, u32>, - ac_expiration_storage: &'hw mut dyn NvramStorage<'hw, u32>, - ac_policy_storage: &'hw mut dyn NvramStorage<'hw, u32>, - dc_expiration_storage: &'hw mut dyn NvramStorage<'hw, u32>, - dc_policy_storage: &'hw mut dyn NvramStorage<'hw, u32>, - ) -> Result<&'hw Service<'hw>, DatetimeClockError> { - info!("Starting time-alarm service task"); - - let service = service_storage.get_or_init(|| Service { +impl<'hw> ServiceInner<'hw> { + fn new(init_params: InitParams<'hw>) -> Self { + Self { clock_state: Mutex::new(RefCell::new(ClockState { - datetime_clock: backing_clock, - tz_data: TimeZoneData::new(tz_storage), + datetime_clock: init_params.backing_clock, + tz_data: TimeZoneData::new(init_params.tz_storage), })), power_source_signal: Signal::new(), timers: Timers::new( - ac_expiration_storage, - ac_policy_storage, - dc_expiration_storage, - dc_policy_storage, + init_params.ac_expiration_storage, + init_params.ac_policy_storage, + init_params.dc_expiration_storage, + init_params.dc_policy_storage, ), capabilities: { // TODO [CONFIG] We could consider making some of these user-configurable, e.g. if we want to support devices that don't have a battery @@ -153,23 +153,16 @@ impl<'hw> Service<'hw> { caps.set_dc_s5_wake_supported(true); caps }, - }); - - // TODO [POWER_SOURCE] we need to subscribe to messages that tell us if we're on AC or DC power so we can decide which alarms to trigger, but those notifications are not yet implemented - revisit when they are. - // TODO [POWER_SOURCE] if it's possible to learn which power source is active at init time, we should set that one active rather than defaulting to the AC timer. - service.timers.ac_timer.start(&service.clock_state, true)?; - service.timers.dc_timer.start(&service.clock_state, false)?; - - Ok(service) + } } /// Query clock capabilities. Analogous to ACPI TAD's _GRT method. - pub fn get_capabilities(&self) -> TimeAlarmDeviceCapabilities { + fn get_capabilities(&self) -> TimeAlarmDeviceCapabilities { self.capabilities } /// Query the current time. Analogous to ACPI TAD's _GRT method. - pub fn get_real_time(&self) -> Result { + fn get_real_time(&self) -> Result { self.clock_state.lock(|clock_state| { let clock_state = clock_state.borrow(); let datetime = clock_state.datetime_clock.get_current_datetime()?; @@ -183,7 +176,7 @@ impl<'hw> Service<'hw> { } /// Change the current time. Analogous to ACPI TAD's _SRT method. - pub fn set_real_time(&self, timestamp: AcpiTimestamp) -> Result<(), DatetimeClockError> { + fn set_real_time(&self, timestamp: AcpiTimestamp) -> Result<(), DatetimeClockError> { self.clock_state.lock(|clock_state| { let mut clock_state = clock_state.borrow_mut(); clock_state.datetime_clock.set_current_datetime(×tamp.datetime)?; @@ -193,17 +186,17 @@ impl<'hw> Service<'hw> { } /// Query the current wake status. Analogous to ACPI TAD's _GWS method. - pub fn get_wake_status(&self, timer_id: AcpiTimerId) -> TimerStatus { + fn get_wake_status(&self, timer_id: AcpiTimerId) -> TimerStatus { self.timers.get_timer(timer_id).get_wake_status() } /// Clear the current wake status. Analogous to ACPI TAD's _CWS method. - pub fn clear_wake_status(&self, timer_id: AcpiTimerId) { + fn clear_wake_status(&self, timer_id: AcpiTimerId) { self.timers.get_timer(timer_id).clear_wake_status(); } /// Configures behavior when the timer expires while the system is on the other power source. Analogous to ACPI TAD's _STP method. - pub fn set_expired_timer_policy( + fn set_expired_timer_policy( &self, timer_id: AcpiTimerId, policy: AlarmExpiredWakePolicy, @@ -215,16 +208,12 @@ impl<'hw> Service<'hw> { } /// Query current behavior when the timer expires while the system is on the other power source. Analogous to ACPI TAD's _TIP method. - pub fn get_expired_timer_policy(&self, timer_id: AcpiTimerId) -> AlarmExpiredWakePolicy { + fn get_expired_timer_policy(&self, timer_id: AcpiTimerId) -> AlarmExpiredWakePolicy { self.timers.get_timer(timer_id).get_timer_wake_policy() } /// Change the expiry time for the given timer. Analogous to ACPI TAD's _STV method. - pub fn set_timer_value( - &self, - timer_id: AcpiTimerId, - timer_value: AlarmTimerSeconds, - ) -> Result<(), DatetimeClockError> { + fn set_timer_value(&self, timer_id: AcpiTimerId, timer_value: AlarmTimerSeconds) -> Result<(), DatetimeClockError> { let new_expiration_time = match timer_value { AlarmTimerSeconds::DISABLED => None, AlarmTimerSeconds(secs) => { @@ -245,7 +234,7 @@ impl<'hw> Service<'hw> { } /// Query the expiry time for the given timer. Analogous to ACPI TAD's _TIV method. - pub fn get_timer_value(&self, timer_id: AcpiTimerId) -> Result { + fn get_timer_value(&self, timer_id: AcpiTimerId) -> Result { let expiration_time = self.timers.get_timer(timer_id).get_expiration_time(); match expiration_time { Some(expiration_time) => { @@ -263,17 +252,6 @@ impl<'hw> Service<'hw> { } } - pub(crate) async fn run_service(&'hw self) -> ! { - loop { - embassy_futures::select::select3( - self.handle_power_source_updates(), - self.handle_timer(AcpiTimerId::AcPower), - self.handle_timer(AcpiTimerId::DcPower), - ) - .await; - } - } - async fn handle_power_source_updates(&'hw self) -> ! { loop { let new_power_source = self.power_source_signal.wait().await; @@ -311,6 +289,117 @@ impl<'hw> Service<'hw> { } } +/// The memory resources required by the time/alarm service. +pub struct Resources<'hw> { + inner: Option>, +} + +impl<'hw> embedded_services::service::ServiceResources for Resources<'hw> { + /// Allocate storage for the service's resources. + fn new() -> Self { + Resources { inner: None } + } +} + +/// A task runner for the time/alarm service. Users of the service must run this object in an embassy task or similar async execution context. +pub struct Runner<'hw> { + service: &'hw ServiceInner<'hw>, +} + +impl<'hw> embedded_services::service::ServiceRunner<'hw> for Runner<'hw> { + /// Run the service. + async fn run(self) -> embedded_services::Never { + loop { + embassy_futures::select::select3( + self.service.handle_power_source_updates(), + self.service.handle_timer(AcpiTimerId::AcPower), + self.service.handle_timer(AcpiTimerId::DcPower), + ) + .await; + } + } +} + +/// Control handle for the time-alarm service. Use this to manipulate the time on the service. +pub struct Service<'hw> { + inner: &'hw ServiceInner<'hw>, +} + +impl<'hw> Service<'hw> { + pub fn get_capabilities(&self) -> TimeAlarmDeviceCapabilities { + self.inner.get_capabilities() + } + + /// Query the current time. Analogous to ACPI TAD's _GRT method. + pub fn get_real_time(&self) -> Result { + self.inner.get_real_time() + } + + /// Change the current time. Analogous to ACPI TAD's _SRT method. + pub fn set_real_time(&self, timestamp: AcpiTimestamp) -> Result<(), DatetimeClockError> { + self.inner.set_real_time(timestamp) + } + + /// Query the current wake status. Analogous to ACPI TAD's _GWS method. + pub fn get_wake_status(&self, timer_id: AcpiTimerId) -> TimerStatus { + self.inner.get_wake_status(timer_id) + } + + /// Clear the current wake status. Analogous to ACPI TAD's _CWS method. + pub fn clear_wake_status(&self, timer_id: AcpiTimerId) { + self.inner.clear_wake_status(timer_id); + } + + /// Configures behavior when the timer expires while the system is on the other power source. Analogous to ACPI TAD's _STP method. + pub fn set_expired_timer_policy( + &self, + timer_id: AcpiTimerId, + policy: AlarmExpiredWakePolicy, + ) -> Result<(), DatetimeClockError> { + self.inner.set_expired_timer_policy(timer_id, policy) + } + + /// Query current behavior when the timer expires while the system is on the other power source. Analogous to ACPI TAD's _TIP method. + pub fn get_expired_timer_policy(&self, timer_id: AcpiTimerId) -> AlarmExpiredWakePolicy { + self.inner.get_expired_timer_policy(timer_id) + } + + /// Change the expiry time for the given timer. Analogous to ACPI TAD's _STV method. + pub fn set_timer_value( + &self, + timer_id: AcpiTimerId, + timer_value: AlarmTimerSeconds, + ) -> Result<(), DatetimeClockError> { + self.inner.set_timer_value(timer_id, timer_value) + } + + /// Query the expiry time for the given timer. Analogous to ACPI TAD's _TIV method. + pub fn get_timer_value(&self, timer_id: AcpiTimerId) -> Result { + self.inner.get_timer_value(timer_id) + } +} + +impl<'hw> embedded_services::service::Service<'hw> for Service<'hw> { + type Runner = Runner<'hw>; + type ErrorType = DatetimeClockError; + type InitParams = InitParams<'hw>; + type Resources = Resources<'hw>; + + async fn new( + service_storage: &'hw mut Resources<'hw>, + init_params: Self::InitParams, + ) -> Result<(Self, Runner<'hw>), DatetimeClockError> { + let service = service_storage.inner.insert(ServiceInner::new(init_params)); + + // TODO [POWER_SOURCE] we need to subscribe to messages that tell us if we're on AC or DC power so we can decide which alarms to trigger, but those notifications are not yet implemented - revisit when they are. + // TODO [POWER_SOURCE] if it's possible to learn which power source is active at init time, we should set that one active rather than defaulting to the AC timer. + service.timers.ac_timer.start(&service.clock_state, true)?; + service.timers.dc_timer.start(&service.clock_state, false)?; + + Ok((Self { inner: service }, Runner { service })) + } +} + impl<'hw> embedded_services::relay::mctp::RelayServiceHandlerTypes for Service<'hw> { type RequestType = AcpiTimeAlarmRequest; type ResultType = AcpiTimeAlarmResult; diff --git a/time-alarm-service/src/task.rs b/time-alarm-service/src/task.rs deleted file mode 100644 index a45fdf62..00000000 --- a/time-alarm-service/src/task.rs +++ /dev/null @@ -1,9 +0,0 @@ -use crate::Service; -use embedded_services::info; - -/// Call this from a dedicated async task. Must be called exactly once on each service instance. -/// Note that on-device, 'hw must be 'static. We're generic over 'hw to enable some test scenarios leveraging tokio and mocks. -pub async fn run_service<'hw>(service: &'hw Service<'hw>) -> ! { - info!("Starting time-alarm service task"); - service.run_service().await -} diff --git a/time-alarm-service/tests/tad_test.rs b/time-alarm-service/tests/tad_test.rs index dc3d43d8..8eabb764 100644 --- a/time-alarm-service/tests/tad_test.rs +++ b/time-alarm-service/tests/tad_test.rs @@ -6,9 +6,9 @@ mod common; #[cfg(test)] mod test { - use embassy_sync::once_lock::OnceLock; use embassy_time::Timer; use embedded_mcu_hal::time::{Datetime, DatetimeClock}; + use embedded_services::service::{Service, ServiceResources, ServiceRunner}; use time_alarm_service_messages as msg; @@ -23,16 +23,18 @@ mod test { let mut dc_pol_storage = MockNvramStorage::new(0); let mut clock = MockDatetimeClock::new_running(); + let mut storage = time_alarm_service::Resources::new(); - let storage = OnceLock::new(); - let service = time_alarm_service::Service::init( - &storage, - &mut clock, - &mut tz_storage, - &mut ac_exp_storage, - &mut ac_pol_storage, - &mut dc_exp_storage, - &mut dc_pol_storage, + let (service, runner) = time_alarm_service::Service::new( + &mut storage, + time_alarm_service::InitParams { + backing_clock: &mut clock, + tz_storage: &mut tz_storage, + ac_expiration_storage: &mut ac_exp_storage, + ac_policy_storage: &mut ac_pol_storage, + dc_expiration_storage: &mut dc_exp_storage, + dc_policy_storage: &mut dc_pol_storage, + }, ) .await .unwrap(); @@ -45,7 +47,7 @@ mod test { // return !, so we should go until the test arm completes and then shut down. // tokio::select! { - _ = time_alarm_service::task::run_service(service) => unreachable!("time alarm service task finished unexpectedly"), + _ = runner.run() => unreachable!("time alarm service task finished unexpectedly"), _ = async { let delay_secs = 2; let begin = service.get_real_time().unwrap(); @@ -73,21 +75,24 @@ mod test { .set_current_datetime(&Datetime::from_unix_time_seconds(TEST_UNIX_TIME)) .unwrap(); - let storage = OnceLock::new(); - let service = time_alarm_service::Service::init( - &storage, - &mut clock, - &mut tz_storage, - &mut ac_exp_storage, - &mut ac_pol_storage, - &mut dc_exp_storage, - &mut dc_pol_storage, + let mut storage = time_alarm_service::Resources::new(); + + let (service, runner) = time_alarm_service::Service::new( + &mut storage, + time_alarm_service::InitParams { + backing_clock: &mut clock, + tz_storage: &mut tz_storage, + ac_expiration_storage: &mut ac_exp_storage, + ac_policy_storage: &mut ac_pol_storage, + dc_expiration_storage: &mut dc_exp_storage, + dc_policy_storage: &mut dc_pol_storage, + }, ) .await .unwrap(); tokio::select! { - _ = time_alarm_service::task::run_service(service) => unreachable!("time alarm service task finished unexpectedly"), + _ = runner.run() => unreachable!("time alarm service task finished unexpectedly"), _ = async { // Clock is paused, so time shouldn't advance unless we set it. let begin = service.get_real_time().unwrap();