summaryrefslogtreecommitdiff
path: root/src/sync.rs
blob: f13f415d45723eac895f0695a2e97f88e6d758f1 (plain)
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
127
128
129
130
131
132
133
134
135
136
137
138
139
//! # Synchronization module
//!
//! Provides synchronization objects for thread-safe memory sharing.
use core::cell::UnsafeCell;
use core::marker::{Send, Sized, Sync};
use core::ops::FnOnce;

/// # Synchronization interfaces
///
/// Provides Synchronization traits.
pub mod interface {
    use core::ops::FnOnce;
    /// # Mutex Trait
    ///
    /// Basic Locking primitive to allow single-process access to data
    pub trait Mutex {
        /// # The data
        ///
        /// Each mutex protects some internal data from modification across
        /// processes when it is in use. This is important if the process
        /// is preempted while the function is using it.
        type Data;
        /// # Locking mechanism
        ///
        /// Locks the mutex to access the data in a closure.
        /// The data can be read and modified in this closure without worry
        /// of poisoning the data across processes.
        fn lock<'a, R>(&'a self, f: impl FnOnce(&'a mut Self::Data) -> R) -> R;
    }
    /// # Initializable
    ///
    /// Initializable type
    pub trait Initializable {
        fn init(&mut self);
    }
}

/// # Basic Lock Structure
pub struct NullLock<T>
where
    T: ?Sized,
{
    /// The internal data to safely share
    data: UnsafeCell<T>,
}

/// # Allow thread sharing
unsafe impl<T> Send for NullLock<T> where T: ?Sized + Send {}
/// # Allow thread sharing
unsafe impl<T> Sync for NullLock<T> where T: ?Sized + Send {}

impl<T> NullLock<T> {
    /// # Create a new instance of the lock
    pub const fn new(data: T) -> Self {
        Self {
            data: UnsafeCell::new(data),
        }
    }
}

impl<T: interface::Initializable> NullLock<T> {
    pub fn init(&self) {
        use interface::Mutex;
        self.lock(|initializable| {
            initializable.init();
        });
    }
}

impl<T> interface::Mutex for NullLock<T> {
    /// # Underlying data of the lock
    type Data = T;

    /// # Locking mechanism
    ///
    /// Locks the Mutex, and passes a mutable reference
    /// to the encapsulated data to a closure.
    fn lock<'a, R>(&'a self, f: impl FnOnce(&'a mut T) -> R) -> R {
        let data = unsafe { &mut *self.data.get() };

        f(data)
    }
}

use core::sync::atomic::{AtomicBool, Ordering};

pub struct SpinLock<T>
where
    T: Sized,
{
    data: UnsafeCell<T>,
    lock: AtomicBool,
}

unsafe impl<T> Send for SpinLock<T> where T: Sized + Send {}
unsafe impl<T> Sync for SpinLock<T> where T: Sized + Send {}

impl<T> SpinLock<T> {
    /// # New lock
    pub const fn new(data: T) -> Self {
        Self {
            data: UnsafeCell::new(data),
            lock: AtomicBool::new(false),
        }
    }
}

impl<T: interface::Initializable> SpinLock<T> {
    /// # Init
    pub fn init(&self) {
        use interface::Mutex;
        self.lock(|init| {
            init.init();
        });
    }
}

impl<T> interface::Mutex for SpinLock<T> {
    /// # Data type
    type Data = T;
    /// # Lock
    fn lock<'a, R>(&'a self, f: impl FnOnce(&'a mut T) -> R) -> R {
        loop {
            // Loop until acquired the lock
	    match self.lock
                      .compare_exchange(false, true, Ordering::Acquire, Ordering::Acquire)
            {
	        Ok(_) => { break; }
	    	_ => {}
            }
        }
        let data = unsafe { &mut *self.data.get() };

        let res = f(data);
        // Release the lock after finished with the underlying data
        self.lock.store(false, Ordering::Release);
        res
    }
}