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}