|
8 | 8 | // option. This file may not be copied, modified, or distributed
|
9 | 9 | // except according to those terms.
|
10 | 10 |
|
11 |
| -use cell::UnsafeCell; |
| 11 | +use fortanix_sgx_abi::Tcs; |
| 12 | + |
| 13 | +use super::abi::thread; |
| 14 | + |
| 15 | +use super::waitqueue::{WaitVariable, WaitQueue, SpinMutex, NotifiedTcs, try_lock_or_false}; |
12 | 16 |
|
13 | 17 | pub struct Mutex {
|
14 |
| - locked: UnsafeCell<bool>, |
| 18 | + inner: SpinMutex<WaitVariable<bool>>, |
15 | 19 | }
|
16 | 20 |
|
17 |
| -unsafe impl Send for Mutex {} |
18 |
| -unsafe impl Sync for Mutex {} // FIXME |
19 |
| - |
| 21 | +// Implementation according to “Operating Systems: Three Easy Pieces”, chapter 28 |
20 | 22 | impl Mutex {
|
| 23 | + #[unstable(feature = "sgx_internals", issue = "0")] // FIXME: min_const_fn |
21 | 24 | pub const fn new() -> Mutex {
|
22 |
| - Mutex { locked: UnsafeCell::new(false) } |
| 25 | + Mutex { inner: SpinMutex::new(WaitVariable::new(false)) } |
23 | 26 | }
|
24 | 27 |
|
25 | 28 | #[inline]
|
26 |
| - pub unsafe fn init(&mut self) { |
27 |
| - } |
| 29 | + pub unsafe fn init(&mut self) {} |
28 | 30 |
|
29 | 31 | #[inline]
|
30 | 32 | pub unsafe fn lock(&self) {
|
31 |
| - let locked = self.locked.get(); |
32 |
| - assert!(!*locked, "cannot recursively acquire mutex"); |
33 |
| - *locked = true; |
| 33 | + let mut guard = self.inner.lock(); |
| 34 | + if *guard.lock_var() { |
| 35 | + // Another thread has the lock, wait |
| 36 | + WaitQueue::wait(guard) |
| 37 | + // Another thread has passed the lock to us |
| 38 | + } else { |
| 39 | + // We are just now obtaining the lock |
| 40 | + *guard.lock_var_mut() = true; |
| 41 | + } |
34 | 42 | }
|
35 | 43 |
|
36 | 44 | #[inline]
|
37 | 45 | pub unsafe fn unlock(&self) {
|
38 |
| - *self.locked.get() = false; |
| 46 | + let guard = self.inner.lock(); |
| 47 | + if let Err(mut guard) = WaitQueue::notify_one(guard) { |
| 48 | + // No other waiters, unlock |
| 49 | + *guard.lock_var_mut() = false; |
| 50 | + } else { |
| 51 | + // There was a thread waiting, just pass the lock |
| 52 | + } |
39 | 53 | }
|
40 | 54 |
|
41 | 55 | #[inline]
|
42 | 56 | pub unsafe fn try_lock(&self) -> bool {
|
43 |
| - let locked = self.locked.get(); |
44 |
| - if *locked { |
| 57 | + let mut guard = try_lock_or_false!(self.inner); |
| 58 | + if *guard.lock_var() { |
| 59 | + // Another thread has the lock |
45 | 60 | false
|
46 | 61 | } else {
|
47 |
| - *locked = true; |
| 62 | + // We are just now obtaining the lock |
| 63 | + *guard.lock_var_mut() = true; |
48 | 64 | true
|
49 | 65 | }
|
50 | 66 | }
|
51 | 67 |
|
52 | 68 | #[inline]
|
53 |
| - pub unsafe fn destroy(&self) { |
54 |
| - } |
| 69 | + pub unsafe fn destroy(&self) {} |
| 70 | +} |
| 71 | + |
| 72 | +struct ReentrantLock { |
| 73 | + owner: Option<Tcs>, |
| 74 | + count: usize |
55 | 75 | }
|
56 | 76 |
|
57 |
| -// FIXME |
58 | 77 | pub struct ReentrantMutex {
|
| 78 | + inner: SpinMutex<WaitVariable<ReentrantLock>>, |
59 | 79 | }
|
60 | 80 |
|
61 | 81 | impl ReentrantMutex {
|
62 |
| - pub unsafe fn uninitialized() -> ReentrantMutex { |
63 |
| - ReentrantMutex { } |
| 82 | + #[unstable(feature = "sgx_internals", issue = "0")] // FIXME: min_const_fn |
| 83 | + pub const fn uninitialized() -> ReentrantMutex { |
| 84 | + ReentrantMutex { |
| 85 | + inner: SpinMutex::new(WaitVariable::new(ReentrantLock { owner: None, count: 0 })) |
| 86 | + } |
64 | 87 | }
|
65 | 88 |
|
| 89 | + #[inline] |
66 | 90 | pub unsafe fn init(&mut self) {}
|
67 | 91 |
|
68 |
| - pub unsafe fn lock(&self) {} |
| 92 | + #[inline] |
| 93 | + pub unsafe fn lock(&self) { |
| 94 | + let mut guard = self.inner.lock(); |
| 95 | + match guard.lock_var().owner { |
| 96 | + Some(tcs) if tcs != thread::current() => { |
| 97 | + // Another thread has the lock, wait |
| 98 | + WaitQueue::wait(guard); |
| 99 | + // Another thread has passed the lock to us |
| 100 | + }, |
| 101 | + _ => { |
| 102 | + // We are just now obtaining the lock |
| 103 | + guard.lock_var_mut().owner = Some(thread::current()); |
| 104 | + guard.lock_var_mut().count += 1; |
| 105 | + }, |
| 106 | + } |
| 107 | + } |
69 | 108 |
|
70 | 109 | #[inline]
|
71 |
| - pub unsafe fn try_lock(&self) -> bool { |
72 |
| - true |
| 110 | + pub unsafe fn unlock(&self) { |
| 111 | + let mut guard = self.inner.lock(); |
| 112 | + if guard.lock_var().count > 1 { |
| 113 | + guard.lock_var_mut().count -= 1; |
| 114 | + } else { |
| 115 | + match WaitQueue::notify_one(guard) { |
| 116 | + Err(mut guard) => { |
| 117 | + // No other waiters, unlock |
| 118 | + guard.lock_var_mut().count = 0; |
| 119 | + guard.lock_var_mut().owner = None; |
| 120 | + }, |
| 121 | + Ok(mut guard) => { |
| 122 | + // There was a thread waiting, just pass the lock |
| 123 | + if let NotifiedTcs::Single(tcs) = guard.notified_tcs() { |
| 124 | + guard.lock_var_mut().owner = Some(tcs) |
| 125 | + } else { |
| 126 | + unreachable!() // called notify_one |
| 127 | + } |
| 128 | + } |
| 129 | + } |
| 130 | + } |
73 | 131 | }
|
74 | 132 |
|
75 |
| - pub unsafe fn unlock(&self) {} |
| 133 | + #[inline] |
| 134 | + pub unsafe fn try_lock(&self) -> bool { |
| 135 | + let mut guard = try_lock_or_false!(self.inner); |
| 136 | + match guard.lock_var().owner { |
| 137 | + Some(tcs) if tcs != thread::current() => { |
| 138 | + // Another thread has the lock |
| 139 | + false |
| 140 | + }, |
| 141 | + _ => { |
| 142 | + // We are just now obtaining the lock |
| 143 | + guard.lock_var_mut().owner = Some(thread::current()); |
| 144 | + guard.lock_var_mut().count += 1; |
| 145 | + true |
| 146 | + }, |
| 147 | + } |
| 148 | + } |
76 | 149 |
|
| 150 | + #[inline] |
77 | 151 | pub unsafe fn destroy(&self) {}
|
78 | 152 | }
|
0 commit comments