ruspiro_lock/sync/spinlock.rs
1/***********************************************************************************************************************
2 * Copyright (c) 2020 by the authors
3 *
4 * Author: André Borrmann <pspwizard@gmx.de>
5 * License: Apache License 2.0 / MIT
6 **********************************************************************************************************************/
7
8//! # Spinlock
9//! Providing simple atomic Spinlock. This can be used to ensure cross core atomic access to data that is typically
10//! shared between them. For example MMIO mapped registers that allow access to peripherals. Please note that usage
11//! of Spinlocks on Raspberry Pi is only safe if the MMU has ben configured properly. Otherwise the cores trying to
12//! aquire a lock will just hang, even if the lock would be available to them.
13//!
14//! # Example
15//! ```
16//! use ruspiro_lock::sync::Spinlock;
17//!
18//! static LOCK: Spinlock = Spinlock::new();
19//!
20//! fn main () {
21//! LOCK.aquire(); // will only return if the lock could be set
22//! // do something
23//!
24//! LOCK.release(); // releasing the lock
25//! }
26//! ```
27use core::arch::asm;
28use core::sync::atomic::{AtomicBool, Ordering};
29
30/// A blocking cross core lock to guarantee mutual exclusive access. While this lock might block other cores
31/// to continue processing this lock should be held as short as possible. Also care shall be taken
32/// while using this lock within interrupt handlers, as this might lead to deadlock situations if the
33/// lock holding core is interrupted and the interrupt is also trying to aquire the same lock.
34#[derive(Debug)]
35#[repr(C, align(16))]
36pub struct Spinlock {
37 flag: AtomicBool,
38}
39
40impl Spinlock {
41 /// Create a new Spinlock. To ensure it is shared between cores, it's typically assigned to a static variable
42 /// # Example
43 /// ```
44 /// # use ruspiro_lock::sync::Spinlock;
45 /// static LOCK: Spinlock = Spinlock::new();
46 /// ```
47 pub const fn new() -> Spinlock {
48 Spinlock {
49 flag: AtomicBool::new(false),
50 }
51 }
52
53 /// Aquire a spinlock. This will block the current core until the lock could be aquired.
54 /// # Example
55 /// ```no_run
56 /// # use ruspiro_lock::sync::Spinlock;
57 /// static LOCK: Spinlock = Spinlock::new();
58 /// # fn main() {
59 /// LOCK.aquire();
60 /// // execution continues only if the lock could be aquired
61 /// # }
62 /// ```
63 #[inline]
64 pub fn aquire(&self) {
65 // set the atomic value to true if it has been false before (set the lock)
66 while self
67 .flag
68 .compare_exchange(false, true, Ordering::SeqCst, Ordering::Acquire)
69 .is_err()
70 {}
71
72 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
73 unsafe {
74 // dmb required before allow access to the protected resource, see:
75 // http://infocenter.arm.com/help/topic/com.arm.doc.dht0008a/DHT0008A_arm_synchronization_primitives.pdf
76 asm!("dmb sy");
77 }
78 }
79
80 /// Release an aquired spinlock.
81 /// # Example
82 /// ```no_run
83 /// # use ruspiro_lock::sync::Spinlock;
84 /// static LOCK: Spinlock = Spinlock::new();
85 /// # fn main() {
86 /// LOCK.release();
87 /// # }
88 /// ```
89 #[inline]
90 pub fn release(&self) {
91 self.flag.store(false, Ordering::SeqCst);
92
93 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
94 unsafe {
95 // dmb required before allow access to the protected resource, see:
96 // http://infocenter.arm.com/help/topic/com.arm.doc.dht0008a/DHT0008A_arm_synchronization_primitives.pdf
97 asm!("dmb sy");
98 // also raise a signal to indicate the spinlock has been changed (this trigger all WFE's to continue
99 // processing) but do data syncronisation barrier upfront to ensure any data updates has been finished
100 asm!(
101 "dsb sy
102 sev"
103 );
104 }
105 }
106}