1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
/***********************************************************************************************************************
* Copyright (c) 2019 by the authors
*
* Author: André Borrmann
* License: MIT / Apache License 2.0
**********************************************************************************************************************/
#![doc(html_root_url = "https://docs.rs/ruspiro-singleton/0.4.3")]
#![no_std]
#![feature(const_fn_trait_bound)]
//! # Singleton pattern implementation
//!
//! Provide a cross core synchronisation safe singleton implementation pattern. The `Singleton` is intended to be used
//! to declare crate level static variables that require safe access accross cores. This is helpful where the data
//! structure used within the `Singleton` represents a peripheral where the crate shall only hand out a single instance
//! to safely represent to unique existance of the peripheral.
//!
//! # HINT
//! Safe lazy initialization is ensured using atomics. On the Raspberry Pi atmomic operations require the *MMU* to be
//! configured and active. Otherwise the executing CPU core will hang when trying to execute the atomic operation.
//!
//! # Example
//! ```no_run
//! # use ruspiro_singleton::*;
//! // define the static variable with an inizialization closure
//! static FOO:Singleton<u32> = Singleton::new(20);
//!
//! // define the static variable with an inizialization closure
//! static DEMO:Singleton<Box<Demo>> = Singleton::lazy(&|| {
//! Box::new(
//! Demo::new()
//! )
//! });
//!
//! // define the type to be accessible as singleton
//! struct Demo {
//! pub count: u32,
//! }
//!
//! // implement the type that should provided as singlton
//! impl Demo {
//! pub const fn new() -> Self {
//! Demo {
//! count: 0,
//! }
//! }
//! }
//!
//! fn main() {
//! // safely use the singleton inside the closure passed to [with_mut] to update it's contents
//! DEMO.with_mut(|d| {
//! d.count += 10;
//! });
//!
//! // safely use the singleton inside the closure passed to [with_ref] if read-only access is required
//! DEMO.with_mut(|d| {
//! println!("Value: {}", d.count);
//! });
//!
//! // you may also return a value from the singleton to work with it after the safe singleton access
//! let val = DEMO.with_ref(|d| {
//! if d.count != 0 {
//! true
//! } else {
//! false
//! }
//! });
//! }
//! ```
mod lazy;
use lazy::LazyValue;
use ruspiro_lock::RWLock;
/// The Singleton wrapper stores any type
pub struct Singleton<T: 'static> {
/// the inner value wrapping the contained data for safe read/write access
inner: RWLock<LazyValue<T>>,
}
// The Singleton need to implement Send & Sync to ensure cross core compile check mechanics
// this is safe as the inner RWLock ensures cross core safety
// but we need to be conditional on the inner type to prevent interior mutable types beeing used
// inside a singleton
unsafe impl<T> Sync for Singleton<T> where T: Sync {}
unsafe impl<T> Send for Singleton<T> where T: Send {}
impl<T: 'static> Singleton<T> {
/// Create a new [Singleton] instance to be used in a static variable. Only ``const fn`` constructors are allowed
/// here.
/// # Example
/// ```no_run
/// # use ruspiro_singleton::*;
/// static FOO: Singleton<u32> = Singleton::new(20);
/// # fn main() {}
/// ```
pub const fn new(value: T) -> Self {
Singleton {
inner: RWLock::new(LazyValue::with_value(value)),
}
}
/// Create a new [Singleton] instance passing a closure that will be evaluated at first access to the contents of
/// the singleton that will provide its value
/// # Example
/// ```no_run
/// # use ruspiro_singleton::*;
/// static FOO: Singleton<String> = Singleton::lazy(&|| String::from("foo"));
/// # fn main() {}
/// ```
pub const fn lazy<F>(init: &'static F) -> Self
where
F: Fn() -> T,
{
Self {
inner: RWLock::new(LazyValue::with_init(init)),
}
}
/// Take the stored singleton for whatever operation and prevent usage by other cores
/// Safe access to the singleton mutable instance is guarantied inside the given closure.
///
pub fn with_mut<F, R>(&self, f: F) -> R
where
F: FnOnce(&mut T) -> R,
{
let inner = self.inner.lock();
// use write lock to mutably access the inner value of the singleton. As long
// as the write lock exists no other write or read lock is possible
let r = f(inner.get_mut());
// explicitly release the lock befor providing the result of the closure to the caller
drop(inner);
r
}
/// Immutable access to a singleton for a specific operation.
/// This access does not enforce any lock nor guarantees safe atomic access to the instance. However, it is usefull
/// in read-only access scenarios like inside interrupt handlers.
///
pub fn with_ref<F, R>(&self, f: F) -> R
where
F: FnOnce(&T) -> R,
{
let inner = self.inner.read();
// multiple read locks are possible when accessing the inner data of the singleton
// all read locks are required to be released before the next write lock could happen
let r = f(inner.get());
// explicitly release the lock befor providing the result of the closure to the caller
drop(inner);
r
}
}