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
/*********************************************************************************************************************** 
 * Copyright (c) 2019 by the authors
 * 
 * Author: André Borrmann 
 * License: Apache License 2.0
 **********************************************************************************************************************/

//! # Spinlock
//! Providing simple atomic Spinlock. This can be used to ensure cross core atomic access to data that is typically
//! shared between them. For example MMIO mapped registers that allow access to peripherals. Please note that usage
//! of Spinlocks on Raspberry Pi is only safe if the MMU has ben configured properly. Otherwise the cores trying to aquire
//! a lock will just hang, even if the lock would be available to them.
//! 
//! # Example
//! ```
//! static LOCK: Spinlock = Spinlock::new();
//! 
//! fn main () {
//!     LOCK.aquire(); // will only return if the lock could be set
//!     // do something
//! 
//!     LOCK.release(); // releasing the lock
//! }
//! ```
use core::sync::atomic::{AtomicBool, Ordering};

#[derive(Debug)]
pub struct Spinlock {
    flag: AtomicBool,
}

impl Spinlock {
    /// Create a new Spinlock. To ensure it is shared between cores, it's typically assigned to a static variable
    /// # Example
    /// ```
    /// static LOCK: Spinlock = Spinlock::new();
    /// ```
    pub const fn new() -> Spinlock {
        Spinlock {
            flag: AtomicBool::new(false),
        }
    }

    pub fn aquire(&self) {
        // set the atomic value to true if it has been false before (set the lock)
        // we need to deactivate interrupts as this wait and the aquired lock should never beeing interrupted
        // otherwise it could lead to deadlocks
        crate::disable_interrupts();
        while self.flag.compare_and_swap(false, true, Ordering::SeqCst) != false { }
    }

    pub fn release(&self) {
        self.flag.store(false, Ordering::SeqCst);
        // re-activate interrupts to the previous enable-state as the lock is now released
        // and no interrupt deadlocks can occur
        crate::re_enable_interrupts();
    }
}