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
/*********************************************************************************************************************** * 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 //! ``` //! 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, Ordering, fence}; use core::cell::Cell; #[derive(Debug)] pub struct Semaphore { flag: AtomicBool, count: Cell<u32>, } impl Semaphore { /// Instantiate a new semaphore with a given initial value /// # Example /// ``` /// let mut sema = Semaphore::new(5); // semaphore could be used/aquired 5 times /// ``` pub const fn new(initial: u32) -> Semaphore { Semaphore { flag: AtomicBool::new(false), count: Cell::new(initial), } } /// increase the inner count of a semaphore allowing it to be used as many times as the inner counters value /// /// # Example /// ``` /// let mut sema = Semaphore::new(0); /// /// sema.up(); // the counter of the semaphore will be increased /// ``` pub fn up(&self) { // ensure atomic access to the count value so it is not updated from other cores while updating // we need to deactivate interrupts as this wait should never beeing interrupted // otherwise it could lead to deadlocks crate::disable_interrupts(); while self.flag.compare_and_swap(false, true, Ordering::Relaxed) != false { } fence(Ordering::Acquire); let c = self.count.get(); self.count.set(c + 1); // release the atomic access self.flag.store(false, Ordering::Release); crate::re_enable_interrupts(); } /// 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 /// ``` /// 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; } // a small hack to force some cpu cycles to pass before the next try // may be a timer wait in the future? for _ in 0..100 { unsafe { asm!("nop") }; } } } /// try to decrease a semaphore for usage. Returns [Ok()] if the semaphore could be used. /// /// # Example /// ``` /// 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<(), &'static str> { // we need to deactivate interrupts as this wait should never beeing interrupted // otherwise it could lead to deadlocks crate::disable_interrupts(); while self.flag.compare_and_swap(false, true, Ordering::Relaxed) != false { } fence(Ordering::Acquire); // try to decrease the counter let c = self.count.get(); let success = if c > 0 { self.count.set(c - 1); true } else { false }; // release the atomic access self.flag.store(false, Ordering::Release); crate::re_enable_interrupts(); if success { Ok(()) } else { Err("unable to use the semaphore, counter is less than 1") } } }