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 {}