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
/*************************************************************************************************** * Copyright (c) 2019 by the authors * * Author: André Borrmann * License: Apache License 2.0 **************************************************************************************************/ //! # Semaphore implementation. //! //! It uses atomic memory locks to ensure the semaphore is exclusively accessed while checking //! or updating it's value. On Raspbarry Pi this will only work if the MMU has been properly configured. Otherwise those //! operations may just hang. //! //! # Example //! ``` //! use ruspiro_lock::Semaphore; //! //! static SEMA: Semaphore = Semaphore::new(1); //! //! fn main () { //! SEMA.down(); // will only return if the counter could be decreased //! // do something //! //! SEMA.up(); // increase the counter for another usage //! } //! ``` use core::sync::atomic::{AtomicBool, AtomicU16, Ordering}; /// Simple counting blocking or non-blocking lock #[derive(Debug)] #[repr(C, align(16))] pub struct Semaphore { flag: AtomicBool, count: AtomicU16, //Cell<u32>, } impl Semaphore { /// Instantiate a new semaphore with a given initial value /// # Example /// ``` /// # use ruspiro_lock::Semaphore; /// # fn doc() { /// let mut sema = Semaphore::new(5); // semaphore could be used/aquired 5 times /// # } /// ``` pub const fn new(initial: u16) -> Semaphore { Semaphore { flag: AtomicBool::new(false), count: AtomicU16::new(initial), //Cell::new(initial), } } /// increase the inner count of a semaphore allowing it to be used as many times as the inner counters value /// /// # Example /// ```no_run /// # use ruspiro_lock::Semaphore; /// # fn doc() { /// let mut sema = Semaphore::new(0); /// sema.up(); // the counter of the semaphore will be increased /// # } /// ``` pub fn up(&self) { while self.flag.compare_and_swap(false, true, Ordering::SeqCst) {} self.count.fetch_add(1, Ordering::AcqRel); self.flag.store(false, Ordering::Release); } /// decrease the inner count of a semaphore. This blocks the current core if the current count is 0 /// and could not beeing decreased. For an unblocking operation use [Semaphore::try_down] /// /// # Example /// ```no_run /// # use ruspiro_lock::Semaphore; /// # fn doc() { /// let sema = Semaphore::new(0); /// sema.down(); /// // if we reache this line, we have used the semaphore and decreased the counter by 1 /// # } /// ``` pub fn down(&self) { loop { if self.try_down().is_ok() { return; } } } /// try to decrease a semaphore for usage. Returns [Ok()] if the semaphore could be used. /// /// # Example /// ``` /// # use ruspiro_lock::Semaphore; /// # fn doc() { /// let sema = Semaphore::new(0); /// if sema.try_down().is_ok() { /// // do something... the counter of the semaphore has been decreased by 1 /// } /// # } /// ``` pub fn try_down(&self) -> Result<(), ()> { // we need to deactivate interrupts as this wait should never beeing interrupted // otherwise it could lead to deadlocks while self.flag.compare_and_swap(false, true, Ordering::SeqCst) {} if self.count.load(Ordering::Acquire) > 0 { self.count.fetch_sub(1, Ordering::AcqRel); self.flag.store(false, Ordering::Release); Ok(()) } else { self.flag.store(false, Ordering::Release); Err(()) } } } unsafe impl Sync for Semaphore {} unsafe impl Send for Semaphore {}