ruspiro_lock/sync/semaphore.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//! # Semaphore implementation.
9//!
10//! It uses atomic memory locks to ensure the semaphore is exclusively accessed while checking
11//! or updating it's value. On Raspbarry Pi this will only work if the MMU has been properly configured. Otherwise those
12//! operations may just hang.
13//!
14//! # Example
15//! ```
16//! use ruspiro_lock::sync::Semaphore;
17//!
18//! static SEMA: Semaphore = Semaphore::new(1);
19//!
20//! fn main () {
21//! SEMA.down(); // will only return if the counter could be decreased
22//! // do something
23//!
24//! SEMA.up(); // increase the counter for another usage
25//! }
26//! ```
27use core::arch::asm;
28use core::sync::atomic::{AtomicU32, Ordering};
29
30/// Simple counting blocking or non-blocking lock
31#[derive(Debug)]
32#[repr(C, align(16))]
33pub struct Semaphore {
34 count: AtomicU32,
35}
36
37impl Semaphore {
38 /// Instantiate a new semaphore with a given initial value
39 /// # Example
40 /// ```
41 /// # use ruspiro_lock::sync::Semaphore;
42 /// # fn doc() {
43 /// let mut sema = Semaphore::new(5); // semaphore could be used/aquired 5 times
44 /// # }
45 /// ```
46 pub const fn new(initial: u32) -> Semaphore {
47 Semaphore {
48 count: AtomicU32::new(initial),
49 }
50 }
51
52 /// increase the inner count of a semaphore allowing it to be used as many times as the inner counters value
53 ///
54 /// # Example
55 /// ```no_run
56 /// # use ruspiro_lock::sync::Semaphore;
57 /// # fn doc() {
58 /// let mut sema = Semaphore::new(0);
59 /// sema.up(); // the counter of the semaphore will be increased
60 /// # }
61 /// ```
62 #[inline]
63 pub fn up(&self) {
64 self.count.fetch_add(1, Ordering::AcqRel);
65
66 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
67 unsafe {
68 // dmb required before allow access to the protected resource, see:
69 // http://infocenter.arm.com/help/topic/com.arm.doc.dht0008a/DHT0008A_arm_synchronization_primitives.pdf
70 asm!("dmb sy");
71 // also raise a signal to indicate the semaphore has been changed (this trigger all WFE's to continue
72 // processing) but do data syncronisation barrier upfront to ensure any data updates has been finished
73 asm!(
74 "dsb sy
75 sev"
76 );
77 }
78 }
79
80 /// decrease the inner count of a semaphore. This blocks the current core if the current count is 0
81 /// and could not beeing decreased. For an unblocking operation use [Semaphore::try_down]
82 ///
83 /// # Example
84 /// ```no_run
85 /// # use ruspiro_lock::sync::Semaphore;
86 /// # fn doc() {
87 /// let sema = Semaphore::new(0);
88 /// sema.down();
89 /// // if we reache this line, we have used the semaphore and decreased the counter by 1
90 /// # }
91 /// ```
92 #[inline]
93 pub fn down(&self) {
94 loop {
95 if self.try_down().is_ok() {
96 return;
97 }
98 // to save energy and cpu consumption we can wait for an event beeing raised that indicates that the
99 // semaphore value has likely beeing changed
100 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
101 unsafe {
102 asm!("wfe");
103 }
104 }
105 }
106
107 /// try to decrease a semaphore for usage. Returns [value@Ok] if the semaphore could be used.
108 ///
109 /// # Example
110 /// ```
111 /// # use ruspiro_lock::sync::Semaphore;
112 /// # fn doc() {
113 /// let sema = Semaphore::new(0);
114 /// if sema.try_down().is_ok() {
115 /// // do something... the counter of the semaphore has been decreased by 1
116 /// }
117 /// # }
118 /// ```
119 #[inline]
120 pub fn try_down(&self) -> Result<(), ()> {
121 let mut value = self.count.load(Ordering::Acquire);
122 if value > 0 {
123 value -= 1;
124 self.count.store(value, Ordering::Release);
125 // dmb required before allow access to the protected resource see:
126 // http://infocenter.arm.com/help/topic/com.arm.doc.dht0008a/DHT0008A_arm_synchronization_primitives.pdf
127 #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
128 unsafe {
129 asm!("dmb sy");
130 }
131 Ok(())
132 } else {
133 // set the current value as "dummy" store to clear the atomic monitor
134 self.count.store(value, Ordering::Release);
135 Err(())
136 }
137 }
138}
139
140impl Default for Semaphore {
141 fn default() -> Self {
142 Semaphore::new(0)
143 }
144}
145
146unsafe impl Sync for Semaphore {}
147unsafe impl Send for Semaphore {}