1 use fortanix_sgx_abi::Tcs;
2 
3 use super::abi::thread;
4 
5 use super::waitqueue::{try_lock_or_false, NotifiedTcs, SpinMutex, WaitQueue, WaitVariable};
6 
7 pub struct Mutex {
8     inner: SpinMutex<WaitVariable<bool>>,
9 }
10 
11 // not movable: see UnsafeList implementation
12 pub type MovableMutex = Box<Mutex>;
13 
14 // Implementation according to “Operating Systems: Three Easy Pieces”, chapter 28
15 impl Mutex {
new() -> Mutex16     pub const fn new() -> Mutex {
17         Mutex { inner: SpinMutex::new(WaitVariable::new(false)) }
18     }
19 
20     #[inline]
init(&mut self)21     pub unsafe fn init(&mut self) {}
22 
23     #[inline]
lock(&self)24     pub unsafe fn lock(&self) {
25         let mut guard = self.inner.lock();
26         if *guard.lock_var() {
27             // Another thread has the lock, wait
28             WaitQueue::wait(guard, || {})
29         // Another thread has passed the lock to us
30         } else {
31             // We are just now obtaining the lock
32             *guard.lock_var_mut() = true;
33         }
34     }
35 
36     #[inline]
unlock(&self)37     pub unsafe fn unlock(&self) {
38         let guard = self.inner.lock();
39         if let Err(mut guard) = WaitQueue::notify_one(guard) {
40             // No other waiters, unlock
41             *guard.lock_var_mut() = false;
42         } else {
43             // There was a thread waiting, just pass the lock
44         }
45     }
46 
47     #[inline]
try_lock(&self) -> bool48     pub unsafe fn try_lock(&self) -> bool {
49         let mut guard = try_lock_or_false!(self.inner);
50         if *guard.lock_var() {
51             // Another thread has the lock
52             false
53         } else {
54             // We are just now obtaining the lock
55             *guard.lock_var_mut() = true;
56             true
57         }
58     }
59 
60     #[inline]
destroy(&self)61     pub unsafe fn destroy(&self) {}
62 }
63 
64 struct ReentrantLock {
65     owner: Option<Tcs>,
66     count: usize,
67 }
68 
69 pub struct ReentrantMutex {
70     inner: SpinMutex<WaitVariable<ReentrantLock>>,
71 }
72 
73 impl ReentrantMutex {
uninitialized() -> ReentrantMutex74     pub const fn uninitialized() -> ReentrantMutex {
75         ReentrantMutex {
76             inner: SpinMutex::new(WaitVariable::new(ReentrantLock { owner: None, count: 0 })),
77         }
78     }
79 
80     #[inline]
init(&self)81     pub unsafe fn init(&self) {}
82 
83     #[inline]
lock(&self)84     pub unsafe fn lock(&self) {
85         let mut guard = self.inner.lock();
86         match guard.lock_var().owner {
87             Some(tcs) if tcs != thread::current() => {
88                 // Another thread has the lock, wait
89                 WaitQueue::wait(guard, || {});
90                 // Another thread has passed the lock to us
91             }
92             _ => {
93                 // We are just now obtaining the lock
94                 guard.lock_var_mut().owner = Some(thread::current());
95                 guard.lock_var_mut().count += 1;
96             }
97         }
98     }
99 
100     #[inline]
unlock(&self)101     pub unsafe fn unlock(&self) {
102         let mut guard = self.inner.lock();
103         if guard.lock_var().count > 1 {
104             guard.lock_var_mut().count -= 1;
105         } else {
106             match WaitQueue::notify_one(guard) {
107                 Err(mut guard) => {
108                     // No other waiters, unlock
109                     guard.lock_var_mut().count = 0;
110                     guard.lock_var_mut().owner = None;
111                 }
112                 Ok(mut guard) => {
113                     // There was a thread waiting, just pass the lock
114                     if let NotifiedTcs::Single(tcs) = guard.notified_tcs() {
115                         guard.lock_var_mut().owner = Some(tcs)
116                     } else {
117                         unreachable!() // called notify_one
118                     }
119                 }
120             }
121         }
122     }
123 
124     #[inline]
try_lock(&self) -> bool125     pub unsafe fn try_lock(&self) -> bool {
126         let mut guard = try_lock_or_false!(self.inner);
127         match guard.lock_var().owner {
128             Some(tcs) if tcs != thread::current() => {
129                 // Another thread has the lock
130                 false
131             }
132             _ => {
133                 // We are just now obtaining the lock
134                 guard.lock_var_mut().owner = Some(thread::current());
135                 guard.lock_var_mut().count += 1;
136                 true
137             }
138         }
139     }
140 
141     #[inline]
destroy(&self)142     pub unsafe fn destroy(&self) {}
143 }
144