From 6bfebd8b29f7917ebe214c308e33b0b65f6c94f6 Mon Sep 17 00:00:00 2001 From: Noah Friedman Date: Wed, 27 Nov 2024 00:39:49 -0500 Subject: [PATCH] Implement `try-init` feature --- esp-hal/CHANGELOG.md | 1 + esp-hal/Cargo.toml | 4 ++++ esp-hal/src/lib.rs | 15 +++++++++++++++ esp-hal/src/peripheral.rs | 19 +++++++++++++++++++ hil-test/Cargo.toml | 2 ++ hil-test/tests/init.rs | 7 +++++++ 6 files changed, 48 insertions(+) diff --git a/esp-hal/CHANGELOG.md b/esp-hal/CHANGELOG.md index 360c1427d71..4de91499b95 100644 --- a/esp-hal/CHANGELOG.md +++ b/esp-hal/CHANGELOG.md @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added `esp_hal::psram::psram_raw_parts` (#2546) - The timer drivers `OneShotTimer` & `PeriodicTimer` have `into_async` and `new_typed` methods (#2586) - `timer::Timer` trait has three new methods, `wait`, `async_interrupt_handler` and `peripheral_interrupt` (#2586) +- Add `try_init` function behind non-default feature `try-init` (#2618) ### Changed diff --git a/esp-hal/Cargo.toml b/esp-hal/Cargo.toml index 68ab0ade890..154ea8f40b7 100644 --- a/esp-hal/Cargo.toml +++ b/esp-hal/Cargo.toml @@ -42,6 +42,7 @@ esp-synopsys-usb-otg = { version = "0.4.2", optional = true, features = ["fs fugit = "0.3.7" log = { version = "0.4.22", optional = true } nb = "1.1.0" +once_cell = { version = "1.20.2", optional = true, default-features = false, features = ["race"] } paste = "1.0.15" portable-atomic = { version = "1.9.0", default-features = false } procmacros = { version = "0.15.0", package = "esp-hal-procmacros", path = "../esp-hal-procmacros" } @@ -152,6 +153,9 @@ octal-psram = [] # This feature is intended for testing; you probably don't want to enable it: ci = ["defmt", "bluetooth"] +# Enable `try_init` -- requires once_cell +try-init = ["once_cell"] + [lints.clippy] mixed_attributes_style = "allow" diff --git a/esp-hal/src/lib.rs b/esp-hal/src/lib.rs index aafe8b21ea8..b689d6844f0 100644 --- a/esp-hal/src/lib.rs +++ b/esp-hal/src/lib.rs @@ -559,3 +559,18 @@ pub fn init(config: Config) -> Peripherals { peripherals } + +/// Attempts to initialize the system. +/// +/// If [`init`] has already been executed, returns [`None`]. +/// (This check is [thread-safe](once_cell::race::OnceBool)) +/// +/// Otherwise, initializes and returns the [`Peripherals`]. +#[cfg(feature = "try-init")] +pub fn try_init(config: Config) -> Option { + if !Peripherals::initialized() { + Some(init(config)) + } else { + None + } +} diff --git a/esp-hal/src/peripheral.rs b/esp-hal/src/peripheral.rs index 4a817111a84..5d55dbec1a0 100644 --- a/esp-hal/src/peripheral.rs +++ b/esp-hal/src/peripheral.rs @@ -257,6 +257,8 @@ mod peripheral_macros { ),* $(,)? ] ) => { + #[cfg(feature = "try-init")] + use once_cell::race::OnceBool; /// Contains the generated peripherals which implement [`Peripheral`] mod peripherals { @@ -293,8 +295,25 @@ mod peripheral_macros { } impl Peripherals { + #[cfg(feature = "try-init")] + const INITIALIZED: OnceBool = OnceBool::new(); + + #[cfg(feature = "try-init")] + pub(crate) fn initialized() -> bool { + Self::INITIALIZED.get().is_some() + } + /// Returns all the peripherals *once* #[inline] + #[cfg(feature = "try-init")] + pub(crate) fn take() -> Self { + if let Ok(_) = Self::INITIALIZED.set(true) { + unsafe { Self::steal() } + } else { + panic!("init called more than once!") + } + } + #[cfg(not(feature = "try-init"))] pub(crate) fn take() -> Self { #[no_mangle] static mut _ESP_HAL_DEVICE_PERIPHERALS: bool = false; diff --git a/hil-test/Cargo.toml b/hil-test/Cargo.toml index cc273714e98..186152cb1c4 100644 --- a/hil-test/Cargo.toml +++ b/hil-test/Cargo.toml @@ -297,6 +297,8 @@ integrated-timers = [ "esp-hal-embassy/integrated-timers", ] octal-psram = ["esp-hal/octal-psram", "esp-alloc"] +# `try-init`: +try-init = ["esp-hal/try-init"] # https://doc.rust-lang.org/cargo/reference/profiles.html#test # Test and bench profiles inherit from dev and release respectively. diff --git a/hil-test/tests/init.rs b/hil-test/tests/init.rs index cec885c7319..d255073a58d 100644 --- a/hil-test/tests/init.rs +++ b/hil-test/tests/init.rs @@ -107,4 +107,11 @@ mod tests { let delay = Delay::new(); delay.delay(2000.millis()); } + + #[cfg(feature = "try-init")] + #[test] + fn test_try_init() { + assert!(esp_hal::try_init(Config::default()).is_some()); + assert!(esp_hal::try_init(Config::default()).is_none()); + } }