spin_sync/mutex8.rs
1// Copyright 2020 Shin Yoshida
2//
3// "LGPL-3.0-or-later OR Apache-2.0 OR BSD-2-Clause"
4//
5// This is part of spin-sync
6//
7// spin-sync is free software: you can redistribute it and/or modify
8// it under the terms of the GNU Lesser General Public License as published by
9// the Free Software Foundation, either version 3 of the License, or
10// (at your option) any later version.
11//
12// spin-sync is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15// GNU Lesser General Public License for more details.
16//
17// You should have received a copy of the GNU Lesser General Public License
18// along with spin-sync. If not, see <http://www.gnu.org/licenses/>.
19//
20//
21// Licensed under the Apache License, Version 2.0 (the "License");
22// you may not use this file except in compliance with the License.
23// You may obtain a copy of the License at
24//
25// http://www.apache.org/licenses/LICENSE-2.0
26//
27// Unless required by applicable law or agreed to in writing, software
28// distributed under the License is distributed on an "AS IS" BASIS,
29// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
30// See the License for the specific language governing permissions and
31// limitations under the License.
32//
33//
34// Redistribution and use in source and binary forms, with or without modification, are permitted
35// provided that the following conditions are met:
36//
37// 1. Redistributions of source code must retain the above copyright notice, this list of
38// conditions and the following disclaimer.
39// 2. Redistributions in binary form must reproduce the above copyright notice, this
40// list of conditions and the following disclaimer in the documentation and/or other
41// materials provided with the distribution.
42//
43// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
44// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
45// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
46// IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
47// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
48// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
49// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
50// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
51// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
52// POSSIBILITY OF SUCH DAMAGE.
53
54use crate::misc::PhantomMutexGuard;
55use crate::result::{TryLockError, TryLockResult};
56use std::fmt::{self, Debug, Display};
57use std::sync::atomic::{AtomicU8, Ordering};
58use std::thread;
59
60/// `Mutex8` is a set of mutexes. Each instance includes 8 mutexes.
61///
62/// The differences between `Mutex8` and [`Mutex`] are as follows.
63///
64/// - `Mutex8` is not template structure. User must make sure to acquire lock before accessing to
65/// the protected object. (Compiler cannot check it.)
66/// - `Mutex8` gives up poisoning strategy. (This feature makes the performance better. It is a
67/// good idea to use `Mutex8` instead of [`Mutex`] for the performance.)
68/// - User can acquire 2 or more than 2 locks of one `Mutex8` instance at once.
69///
70/// [`Mutex`]: struct.Mutex.html
71pub struct Mutex8(AtomicU8);
72
73impl Mutex8 {
74 /// The number of mutexes that one `Mutex8` has.
75 pub const LEN: usize = 8;
76
77 /// Creates a new instance in an unlocked state ready for use.
78 ///
79 /// Unlike to `std::sync::Mutex` , this is a const function.
80 /// It can be use to initialize static variable.
81 ///
82 /// # Examples
83 ///
84 /// Declaring a static variable.
85 ///
86 /// ```
87 /// use spin_sync::Mutex8;
88 ///
89 /// static mutex8: Mutex8 = Mutex8::new();
90 /// ```
91 ///
92 /// Declaring a local variable.
93 ///
94 /// ```
95 /// use spin_sync::Mutex8;
96 ///
97 /// let mutex8 = Mutex8::new();
98 /// ```
99 #[inline]
100 pub const fn new() -> Self {
101 Self(AtomicU8::new(0))
102 }
103}
104impl Display for Mutex8 {
105 #[inline]
106 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107 write!(f, "Mutex8")
108 }
109}
110
111impl Debug for Mutex8 {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 f.debug_struct("Mutex8")
114 .field("locked_bits", &self.locked_bits())
115 .finish()
116 }
117}
118
119impl Mutex8 {
120 /// Blocks the current thread until acquiring the lock(s) indicated by `lock_bits` and returns
121 /// an RAII guard object.
122 ///
123 /// Each bit of `lock_bits` indicates the lock of `Mutex8` . For example, '0x01' corresponds
124 /// to the first lock and '0x02' does to the second lock. If 2 or more than 2 bits are set, the
125 /// `lock_bits` means all of them. In case of '0x03', for example, it means both the first and
126 /// the second locks.
127 ///
128 /// # Examples
129 ///
130 /// ```
131 /// use spin_sync::Mutex8;
132 ///
133 /// let mutex8 = Mutex8::new();
134 ///
135 /// // Acquire '0x01' and '0x02' in order.
136 /// {
137 /// let guard1 = mutex8.lock(0x01);
138 /// let guard2 = mutex8.lock(0x02);
139 /// }
140 ///
141 /// // Acquire '0x01' and '0x02' at the same time.
142 /// {
143 /// let guard3 = mutex8.lock(0x03);
144 /// }
145 /// ```
146 #[inline]
147 pub fn lock(&self, lock_bits: u8) -> Mutex8Guard {
148 let mut expected = 0;
149 while {
150 debug_assert_eq!(0, expected & lock_bits);
151 let locked = expected + lock_bits;
152 let current = self.0.compare_and_swap(expected, locked, Ordering::Acquire);
153
154 if expected == current {
155 // Succeeded to acquire
156 false
157 } else if current & lock_bits == 0 {
158 // The first assumuption was wrong.
159 // Try again soon.
160 expected = current;
161 true
162 } else {
163 // Lock competition.
164 thread::yield_now();
165 true
166 }
167 } {}
168
169 Mutex8Guard {
170 bits: lock_bits,
171 mutex8: &self,
172 _phantom: Default::default(),
173 }
174 }
175
176 /// Attempts to acquire lock(s) indicated by `lock_bits` and returns an RAII guard object if
177 /// succeeded.
178 ///
179 /// Each bit of `lock_bits` indicates the lock of `Mutex8` . For example, '0x01' corresponds
180 /// to the first lock and '0x02' does to the second lock. If 2 or more than 2 bits are set, the
181 /// `lock_bits` means all of them. In case of '0x03', for example, it means both the first and
182 /// the second locks.
183 ///
184 /// Behaves like [`lock`] except for this method returns an error immediately if another user
185 /// is holding the lock.
186 ///
187 /// This method does not block.
188 ///
189 /// # Errors
190 ///
191 /// If another user is holding this mutex, [`TryLockError::WouldBlock`] is returned.
192 ///
193 /// [`lock`]: #method.lock
194 /// [`TryLockError::WouldBlock`]: type.TryLockError.html
195 ///
196 /// # Examples
197 ///
198 /// ```
199 /// use spin_sync::Mutex8;
200 ///
201 /// let mutex8 = Mutex8::new();
202 ///
203 /// // Try to acquire 0x01 twice. The second try will be fail.
204 /// {
205 /// let result1 = mutex8.try_lock(0x01);
206 /// assert_eq!(true, result1.is_ok());
207 ///
208 /// let result2 = mutex8.try_lock(0x01);
209 /// assert_eq!(true, result2.is_err());
210 /// }
211 ///
212 /// // Try to acquire 0x01 and 0x02 at the same time.
213 /// // After that, neither 0x01 nor 0x02 can be locked.
214 /// {
215 /// // Acquire locks 0x01 and 0x02 at once.
216 /// let result1 = mutex8.try_lock(0x03);
217 /// assert_eq!(true, result1.is_ok());
218 ///
219 /// let result2 = mutex8.try_lock(0x01);
220 /// assert_eq!(true, result2.is_err());
221 ///
222 /// let result3 = mutex8.try_lock(0x02);
223 /// assert_eq!(true, result3.is_err());
224 /// }
225 /// ```
226 #[inline]
227 pub fn try_lock(&self, lock_bits: u8) -> TryLockResult<Mutex8Guard> {
228 let mut expected = 0;
229 while {
230 debug_assert_eq!(0, expected & lock_bits);
231 let locked = expected + lock_bits;
232 let current = self.0.compare_and_swap(expected, locked, Ordering::Acquire);
233
234 if expected == current {
235 // Succeeded to acquire
236 false
237 } else if current & lock_bits == 0 {
238 // The first assumuption was wrong.
239 // Try again soon.
240 expected = current;
241 true
242 } else {
243 return Err(TryLockError::WouldBlock);
244 }
245 } {}
246
247 Ok(Mutex8Guard {
248 bits: lock_bits,
249 mutex8: &self,
250 _phantom: Default::default(),
251 })
252 }
253
254 /// Returns the bits that some [`Mutex8Guard`] instance(s) is holding.
255 ///
256 /// # Example
257 ///
258 /// ```
259 /// use spin_sync::Mutex8;
260 ///
261 /// let mutex8 = Mutex8::new();
262 ///
263 /// // Acquire 0x01.
264 /// let guard1 = mutex8.lock(0x01);
265 /// assert_eq!(0x01, mutex8.locked_bits());
266 ///
267 /// // Acquire 0x02.
268 /// let guard2 = mutex8.lock(0x02);
269 /// assert_eq!(0x03, mutex8.locked_bits());
270 ///
271 /// // Acquire 0x04 and 0x08 at the same time.
272 /// let mut guard3 = mutex8.lock(0x0c);
273 /// assert_eq!(0x0f, mutex8.locked_bits());
274 ///
275 /// // Release 0x08.
276 /// guard3.release(0x08);
277 /// assert_eq!(0x07, mutex8.locked_bits());
278 /// ```
279 #[inline]
280 pub fn locked_bits(&self) -> u8 {
281 self.0.load(Ordering::Relaxed)
282 }
283
284 /// The number of mutexes that one `Mutex8` has.
285 pub const fn len(&self) -> usize {
286 Self::LEN
287 }
288}
289
290/// An RAII implementation of a "scoped lock(s)" of a [`Mutex8`] .
291///
292/// When this structure is dropped, all the lock(s) will be released at once.
293///
294/// [`Mutex8`]: struct.Mutex8.html
295#[must_use = "if unused the Mutex8 will immediately unlock"]
296pub struct Mutex8Guard<'a> {
297 bits: u8,
298 mutex8: &'a Mutex8,
299 _phantom: PhantomMutexGuard<'a, u8>, // To implement !Send
300}
301
302impl Drop for Mutex8Guard<'_> {
303 #[inline]
304 fn drop(&mut self) {
305 if self.bits != 0 {
306 self.release(self.bits);
307 }
308 }
309}
310
311impl Display for Mutex8Guard<'_> {
312 #[inline]
313 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
314 write!(f, "Mutex8Guard")
315 }
316}
317
318impl Debug for Mutex8Guard<'_> {
319 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
320 f.debug_struct("Mutex8Guard")
321 .field("lock_bits", &self.lock_bits())
322 .finish()
323 }
324}
325
326impl Mutex8Guard<'_> {
327 /// Releases the lock(s) partially indicated by `lock_bits` .
328 ///
329 /// Each bit of `lock_bits` indicates the lock of [`Mutex8`] . For example, '0x01' corresponds
330 /// to the first lock and '0x02' does to the second lock. If 2 or more than 2 bits are set, the
331 /// `lock_bits` means all of them. In case of '0x03', for example, it means both the first and
332 /// the second locks.
333 ///
334 /// If `lock_bits` is same to that is being holded, `self` releases all the locks; otherwise,
335 /// the others will still be being holded after the method returns.
336 ///
337 /// `lock_bits` must not include a bit that `self` is not holding.
338 ///
339 /// # Panics
340 ///
341 /// Panics if `lock_bits` includes a bit that `self` is not holding.
342 ///
343 /// [`Mutex8`]: struct.Mutex8.html
344 ///
345 /// # Examples
346 ///
347 /// ```
348 /// use spin_sync::Mutex8;
349 ///
350 /// let mutex8 = Mutex8::new();
351 ///
352 /// // Acquire 0x01 and 0x02 at the same time.
353 /// let mut guard = mutex8.lock(0x03);
354 ///
355 /// {
356 /// // Fail to acquire 0x01 again.
357 /// let e = mutex8.try_lock(0x01);
358 /// assert!(e.is_err());
359 ///
360 /// // Fail to acquire 0x02 again.
361 /// let e = mutex8.try_lock(0x02);
362 /// assert!(e.is_err());
363 /// }
364 ///
365 /// // Release only 0x01. (0x02 is left.)
366 /// guard.release(0x01);
367 ///
368 /// {
369 /// // Success to acquire 0x01 now.
370 /// let o = mutex8.try_lock(0x01);
371 /// assert!(o.is_ok());
372 ///
373 /// // Still fail to acquire 0x02.
374 /// let e = mutex8.try_lock(0x02);
375 /// assert!(e.is_err());
376 /// }
377 /// ```
378 #[inline]
379 pub fn release(&mut self, lock_bits: u8) {
380 assert_eq!(lock_bits, self.bits & lock_bits);
381
382 let mut expected = self.bits;
383 while {
384 debug_assert_eq!(self.bits, expected & self.bits);
385 let unlocked = expected - lock_bits;
386 let current = self
387 .mutex8
388 .0
389 .compare_and_swap(expected, unlocked, Ordering::Release);
390
391 if current == expected {
392 // Succeeded to release.
393 self.bits -= lock_bits;
394 false
395 } else {
396 // First assumption was wrong.
397 // Try again.
398 expected = current;
399 true
400 }
401 } {}
402 }
403
404 /// Returns the bits that `self` is holding.
405 ///
406 /// # Example
407 ///
408 /// ```
409 /// use spin_sync::Mutex8;
410 ///
411 /// let mutex8 = Mutex8::new();
412 ///
413 /// // Acquire 0x01 and 0x02 at the same time.
414 /// let mut guard = mutex8.lock(0x03);
415 /// assert_eq!(0x03, guard.lock_bits());
416 ///
417 /// // Release only 0x02. (0x01 is left.)
418 /// guard.release(0x02);
419 /// assert_eq!(0x01, guard.lock_bits());
420 ///
421 /// // Release 0x01. (No lock is left.)
422 /// guard.release(0x01);
423 /// assert_eq!(0x00, guard.lock_bits());
424 /// ```
425 #[inline]
426 pub fn lock_bits(&self) -> u8 {
427 self.bits
428 }
429}