1
//! Authors: Maurice Laveaux, Flip van Spaendonck and Jan Friso Groote
2

            
3
use std::error::Error;
4
use std::fmt::Debug;
5
use std::ops::Deref;
6
use std::ops::DerefMut;
7

            
8
#[cfg(not(loom))]
9
mod inner {
10
    pub(super) use std::cell::UnsafeCell;
11
    pub(super) use std::hint::spin_loop;
12
    pub(super) use std::sync::Arc;
13
    pub(super) use std::sync::Mutex;
14
    pub(super) use std::sync::MutexGuard;
15
    pub(super) use std::sync::TryLockError;
16
    pub(super) use std::sync::atomic::AtomicBool;
17
    pub(super) use std::sync::atomic::Ordering;
18
    pub(super) use std::sync::atomic::fence;
19
}
20

            
21
// We replace the standard implementation by loom's implementation.
22
#[cfg(loom)]
23
mod inner {
24
    pub use std::mem::ManuallyDrop;
25
    pub use std::sync::TryLockError;
26

            
27
    pub use loom::cell::UnsafeCell;
28
    pub use loom::hint::spin_loop;
29
    pub use loom::sync::Arc;
30
    pub use loom::sync::Mutex;
31
    pub use loom::sync::MutexGuard;
32
    pub use loom::sync::atomic::AtomicBool;
33
    pub use loom::sync::atomic::Ordering;
34
    pub use loom::sync::atomic::fence;
35
}
36

            
37
use inner::*;
38

            
39
use crossbeam_utils::CachePadded;
40

            
41
/// A shared mutex (readers-writer lock) implementation based on the so-called
42
/// busy-forbidden protocol.
43
///
44
/// # Details
45
///
46
/// Compared to a regular [std::sync::Mutex] this struct is Send but not Sync.
47
/// This means that every thread must acquire a clone of the shared mutex and
48
/// the cloned instances of the same shared mutex guarantee shared access
49
/// through the `read` operation and exclusive access for the `write` operation
50
/// of the given object.
51
///
52
/// # Poisoning
53
///
54
/// A panic inside a `write` section poisons the internal registration mutex.
55
/// After that, every [`BfSharedMutex::read`] and [`BfSharedMutex::write`]
56
/// returns `Err`; the poison is only cleared once enough clones are dropped.
57
pub struct BfSharedMutex<T> {
58
    /// The local control bits of each instance.
59
    control: Arc<CachePadded<SharedMutexControl>>,
60

            
61
    /// Index into the `other` table.
62
    index: usize,
63

            
64
    /// Information shared between all clones.
65
    shared: Arc<CachePadded<SharedData<T>>>,
66
}
67

            
68
// SAFETY: Sending a BfSharedMutex to another thread transfers ownership of this
69
// clone's control bits along with it; those bits are only ever touched by the
70
// thread that owns the clone. `T` is accessed from whichever thread holds a
71
// clone, so it must be both `Send` and `Sync`.
72
unsafe impl<T: Send + Sync> Send for BfSharedMutex<T> {}
73

            
74
/// The busy and forbidden flags used to implement the protocol.
75
#[derive(Default)]
76
struct SharedMutexControl {
77
    busy: AtomicBool,
78
    forbidden: AtomicBool,
79
}
80

            
81
/// The shared data between all instances of the shared mutex.
82
struct SharedData<T> {
83
    /// The object that is being protected.
84
    object: UnsafeCell<T>,
85

            
86
    /// The list of all the shared mutex instances.
87
    other: Mutex<Vec<Option<Arc<CachePadded<SharedMutexControl>>>>>,
88
}
89

            
90
impl<T> BfSharedMutex<T> {
91
    /// Constructs a new shared mutex for protecting access to the given object.
92
1777
    pub fn new(object: T) -> Self {
93
1777
        let control = Arc::new(CachePadded::new(SharedMutexControl::default()));
94

            
95
1777
        Self {
96
1777
            control: control.clone(),
97
1777
            shared: Arc::new(CachePadded::new(SharedData {
98
1777
                object: UnsafeCell::new(object),
99
1777
                other: Mutex::new(vec![Some(control.clone())]),
100
1777
            })),
101
1777
            index: 0,
102
1777
        }
103
1777
    }
104
}
105

            
106
impl<T> Clone for BfSharedMutex<T> {
107
1806
    fn clone(&self) -> Self {
108
        // Register a new instance in the other list
109
1806
        let control = Arc::new(CachePadded::new(SharedMutexControl::default()));
110

            
111
1806
        let mut other = self.shared.other.lock().expect("Failed to lock mutex");
112
2047
        let index = match other.iter().position(|slot| slot.is_none()) {
113
            Some(index) => {
114
                // Reuse an empty slot if available.
115
                other[index] = Some(control.clone());
116
                index
117
            }
118
            None => {
119
1806
                other.push(Some(control.clone()));
120
1806
                other.len() - 1
121
            }
122
        };
123

            
124
1806
        Self {
125
1806
            control,
126
1806
            index,
127
1806
            shared: self.shared.clone(),
128
1806
        }
129
1806
    }
130
}
131

            
132
impl<T> Drop for BfSharedMutex<T> {
133
1818
    fn drop(&mut self) {
134
        // A panic inside a write section poisons this mutex, since the write guard holds it.
135
        // Deregistering is still safe then, and panicking in Drop during unwinding would abort.
136
1818
        let mut other = self
137
1818
            .shared
138
1818
            .other
139
1818
            .lock()
140
1818
            .unwrap_or_else(std::sync::PoisonError::into_inner);
141

            
142
        // Remove ourselves from the table.
143
1818
        other[self.index] = None;
144

            
145
        // Trim trailing None slots to keep the vec compact.
146
3636
        while other.last().is_some_and(|slot| slot.is_none()) {
147
1818
            other.pop();
148
1818
        }
149
1818
    }
150
}
151

            
152
/// The guard object for exclusive access to the underlying object.
153
#[must_use = "Dropping the guard unlocks the shared mutex immediately"]
154
pub struct BfSharedMutexWriteGuard<'a, T> {
155
    #[allow(dead_code)]
156
    mutex: &'a BfSharedMutex<T>,
157

            
158
    guard: MutexGuard<'a, Vec<Option<Arc<CachePadded<SharedMutexControl>>>>>,
159

            
160
    /// When loom is enabled, we store a write reference tracked by Loom.
161
    #[cfg(loom)]
162
    ptr: ManuallyDrop<loom::cell::MutPtr<T>>,
163
}
164

            
165
/// Allow dereferencing the underlying object.
166
impl<T> Deref for BfSharedMutexWriteGuard<'_, T> {
167
    type Target = T;
168

            
169
5437
    fn deref(&self) -> &Self::Target {
170
        // SAFETY: We are the only guard after `write()`, so immutable access to the underlying
171
        // object is sound.
172
        #[cfg(not(loom))]
173
        unsafe {
174
5437
            &*self.mutex.shared.object.get()
175
        }
176

            
177
        #[cfg(loom)]
178
        unsafe {
179
            self.ptr.deref().deref()
180
        }
181
5437
    }
182
}
183

            
184
impl<T> DerefMut for BfSharedMutexWriteGuard<'_, T> {
185
570358
    fn deref_mut(&mut self) -> &mut Self::Target {
186
        // SAFETY: We are the only guard after `write()`, so exclusive mutable access to the
187
        // underlying object is sound.
188
        #[cfg(not(loom))]
189
570358
        unsafe {
190
570358
            &mut *self.mutex.shared.object.get()
191
570358
        }
192

            
193
        #[cfg(loom)]
194
        unsafe {
195
            self.ptr.deref().deref()
196
        }
197
570358
    }
198
}
199

            
200
impl<T> Drop for BfSharedMutexWriteGuard<'_, T> {
201
570356
    fn drop(&mut self) {
202
        // End Loom's write tracking before releasing the protocol.
203
        #[cfg(loom)]
204
        unsafe {
205
            ManuallyDrop::drop(&mut self.ptr);
206
        }
207

            
208
        // Allow other threads to acquire access to the shared mutex.
209
1221221
        for control in self.guard.iter().flatten() {
210
1221221
            control.forbidden.store(false, Ordering::Release);
211
1221221
        }
212

            
213
        // The mutex guard is then dropped here.
214
570356
    }
215
}
216

            
217
// SAFETY: Sharing &WriteGuard across threads only exposes &T.
218
unsafe impl<T: Sync> Sync for BfSharedMutexWriteGuard<'_, T> {}
219

            
220
#[must_use = "Dropping the guard unlocks the shared mutex immediately"]
221
pub struct BfSharedMutexReadGuard<'a, T> {
222
    mutex: &'a BfSharedMutex<T>,
223

            
224
    /// When loom is enabled, we store a read reference tracked by Loom.
225
    #[cfg(loom)]
226
    ptr: ManuallyDrop<loom::cell::ConstPtr<T>>,
227
}
228

            
229
// SAFETY: Sharing &ReadGuard across threads only exposes &T.
230
unsafe impl<T: Sync> Sync for BfSharedMutexReadGuard<'_, T> {}
231

            
232
/// Allows dereferencing the underlying object.
233
impl<T> Deref for BfSharedMutexReadGuard<'_, T> {
234
    type Target = T;
235

            
236
784383996
    fn deref(&self) -> &Self::Target {
237
        // SAFETY: There can only be shared guards while this guard is alive, so immutable
238
        // access to the object is sound.
239
        #[cfg(not(loom))]
240
        unsafe {
241
784383996
            &*self.mutex.shared.object.get()
242
        }
243

            
244
        #[cfg(loom)]
245
        unsafe {
246
            self.ptr.deref().deref()
247
        }
248
784383996
    }
249
}
250

            
251
impl<T> Drop for BfSharedMutexReadGuard<'_, T> {
252
2257709295
    fn drop(&mut self) {
253
2257709295
        debug_assert!(
254
2257709295
            self.mutex.control.busy.load(Ordering::Relaxed),
255
            "Cannot unlock shared lock that was not acquired"
256
        );
257

            
258
        // End Loom's read tracking before releasing the protocol.
259
        #[cfg(loom)]
260
        unsafe {
261
            ManuallyDrop::drop(&mut self.ptr);
262
        }
263

            
264
        // Release is sufficient, this synchronises the writes of this thread with writer.
265
2257709295
        self.mutex.control.busy.store(false, Ordering::Release);
266
2257709295
    }
267
}
268

            
269
impl<T> BfSharedMutex<T> {
270
    /// Provides read access to the underlying object, allowing multiple immutable references to it.
271
    ///
272
    /// # Panics
273
    ///
274
    /// Panics when called while this instance already holds a read guard. Reentrant reads would
275
    /// corrupt the `busy` flag.
276
294977
    pub fn read<'a>(&'a self) -> Result<BfSharedMutexReadGuard<'a, T>, Box<dyn Error + 'a>> {
277
294977
        self.acquire_shared()?;
278

            
279
        // We now have immutable access to the object due to the protocol.
280
294977
        Ok(BfSharedMutexReadGuard {
281
294977
            mutex: self,
282
294977
            #[cfg(loom)]
283
294977
            ptr: ManuallyDrop::new(self.shared.object.get()),
284
294977
        })
285
294977
    }
286

            
287
    /// Runs the busy-forbidden protocol to acquire shared (read) access, setting this instance's
288
    /// `busy` flag, without materialising a guard.
289
    ///
290
    /// The caller becomes responsible for eventually clearing the `busy` flag, e.g. by dropping a
291
    /// guard reconstructed with [`Self::create_read_guard_unchecked`]. Unlike [`Self::read`], this
292
    /// takes no data borrow of the cell, so under loom it can be paired with `mem::forget`-style
293
    /// ownership transfer (as `RecursiveLock` does) without leaking a read borrow.
294
2257709295
    pub(crate) fn acquire_shared(&self) -> Result<(), Box<dyn Error + '_>> {
295
2257709295
        assert!(
296
2257709295
            !self.control.busy.load(Ordering::Relaxed),
297
            "Cannot acquire read access again inside a reader section"
298
        );
299

            
300
2257709295
        self.control.busy.store(true, Ordering::Relaxed);
301
2257709295
        fence(Ordering::SeqCst);
302
2257711546
        while self.control.forbidden.load(Ordering::Acquire) {
303
            // Signal the writer that this thread is no longer busy, allowing it to make progress.
304
2251
            self.control.busy.store(false, Ordering::Relaxed);
305

            
306
            // For loom with spin locks we must ensure that other threads can make progress for fairness.
307
            #[cfg(loom)]
308
            loom::thread::yield_now();
309

            
310
            // Wait for the mutex of the writer.
311
2251
            let _guard = self.shared.other.lock()?;
312

            
313
            // Allow another thread to acquire the lock.
314
            #[cfg(loom)]
315
            loom::thread::yield_now();
316

            
317
2251
            self.control.busy.store(true, Ordering::Relaxed);
318

            
319
            // The busy store must become visible before the forbidden load is performed, exactly
320
            // as on the initial acquisition above.
321
2251
            fence(Ordering::SeqCst);
322
        }
323

            
324
2257709295
        Ok(())
325
2257709295
    }
326

            
327
    /// Creates a new `BfSharedMutexReadGuard` without checking if the lock is held.
328
    ///
329
    /// # Safety
330
    ///
331
    /// This method must only be called if the thread logically holds a read lock.
332
    ///
333
    /// This function does not increment the read count of the lock. Calling this function when a
334
    /// guard has already been produced is undefined behaviour unless the guard was forgotten
335
    /// with `mem::forget`.
336
2257414318
    pub unsafe fn create_read_guard_unchecked(&self) -> BfSharedMutexReadGuard<'_, T> {
337
2257414318
        BfSharedMutexReadGuard {
338
2257414318
            mutex: self,
339
2257414318
            #[cfg(loom)]
340
2257414318
            ptr: ManuallyDrop::new(self.shared.object.get()),
341
2257414318
        }
342
2257414318
    }
343

            
344
    /// Returns a raw pointer to the underlying data.
345
    ///
346
    /// This is useful when combined with `mem::forget` to hold a lock without
347
    /// the need to maintain a [`BfSharedMutexReadGuard`] or [`BfSharedMutexWriteGuard`] object
348
    /// alive, for example when dealing with FFI.
349
    ///
350
    /// # Safety
351
    ///
352
    /// You must ensure that there are no data races when dereferencing the
353
    /// returned pointer, for example if the current thread logically owns a
354
    /// [`BfSharedMutexReadGuard`] or [`BfSharedMutexWriteGuard`] but that guard has been discarded
355
    /// using `mem::forget`.
356
    #[cfg(not(loom))]
357
131036503
    pub fn data_ptr(&self) -> *mut T {
358
131036503
        self.shared.object.get()
359
131036503
    }
360

            
361
    #[cfg(loom)]
362
    pub fn data_ptr(&self) -> loom::cell::ConstPtr<T> {
363
        self.shared.object.get()
364
    }
365

            
366
    /// Provide write access to the underlying object, only a single mutable reference to the object exists.
367
10197
    pub fn write<'a>(&'a self) -> Result<BfSharedMutexWriteGuard<'a, T>, Box<dyn Error + 'a>> {
368
10197
        let other = self.shared.other.lock()?;
369
10197
        Ok(self.acquire_exclusive(other))
370
10197
    }
371

            
372
    /// Attempts to acquire write access without blocking on the inner registration mutex.
373
    ///
374
    /// Returns `Ok(None)` when that inner mutex is already locked by another thread, which
375
    /// happens while another clone is acquiring or releasing a write lock (or is otherwise
376
    /// inside the protocol's critical section). This lets a caller bail out instead of waiting,
377
    /// for example to skip garbage collection when another thread is already performing it.
378
607101
    pub fn try_write<'a>(&'a self) -> Result<Option<BfSharedMutexWriteGuard<'a, T>>, Box<dyn Error + 'a>> {
379
607101
        let other = match self.shared.other.try_lock() {
380
560160
            Ok(other) => other,
381
46941
            Err(TryLockError::WouldBlock) => return Ok(None),
382
            Err(TryLockError::Poisoned(err)) => return Err(Box::new(err)),
383
        };
384

            
385
560160
        Ok(Some(self.acquire_exclusive(other)))
386
607101
    }
387

            
388
    /// Completes the busy-forbidden protocol for exclusive access once the inner registration
389
    /// mutex has been locked, forbidding all instances and waiting for any busy readers to exit.
390
570357
    fn acquire_exclusive<'a>(
391
570357
        &'a self,
392
570357
        other: MutexGuard<'a, Vec<Option<Arc<CachePadded<SharedMutexControl>>>>>,
393
570357
    ) -> BfSharedMutexWriteGuard<'a, T> {
394
570357
        debug_assert!(
395
570357
            !self.control.busy.load(Ordering::Relaxed),
396
            "Can only exclusive lock outside of a shared lock, no upgrading!"
397
        );
398
570357
        debug_assert!(
399
570357
            !self.control.forbidden.load(Ordering::Relaxed),
400
            "Can not acquire exclusive lock inside of exclusive section"
401
        );
402

            
403
        // Make all instances wait due to forbidden access.
404
1221221
        for control in other.iter().flatten() {
405
1221221
            debug_assert!(
406
1221221
                !control.forbidden.load(Ordering::Relaxed),
407
                "Other instance is already forbidden, this cannot happen"
408
            );
409

            
410
1221220
            control.forbidden.store(true, Ordering::Relaxed);
411
        }
412

            
413
570356
        fence(Ordering::SeqCst);
414

            
415
        // Wait for the instances to exit their busy status.
416
1237593
        for (index, option) in other.iter().enumerate() {
417
1237593
            if index != self.index
418
667237
                && let Some(object) = option
419
            {
420
                // We just synchronize with the busy store of the other instances.
421
2836676
                while object.busy.load(Ordering::Acquire) {
422
2185811
                    spin_loop();
423
2185811
                }
424
586728
            }
425
        }
426

            
427
        // We now have exclusive access to the object according to the protocol
428
570356
        BfSharedMutexWriteGuard {
429
570356
            mutex: self,
430
570356
            guard: other,
431
570356
            #[cfg(loom)]
432
570356
            ptr: ManuallyDrop::new(self.shared.object.get_mut()),
433
570356
        }
434
570356
    }
435

            
436
    /// Check if this instance's read lock is currently held (i.e., this instance's `busy` flag is set).
437
    ///
438
    /// Note: this only reflects the state of **this** clone of the shared mutex. Other clones
439
    /// may independently hold their own read locks.
440
1318419
    pub fn is_locked(&self) -> bool {
441
1318419
        self.control.busy.load(Ordering::Relaxed)
442
1318419
    }
443

            
444
    /// Check if this instance has been forbidden from acquiring a read lock, which indicates
445
    /// that another clone is holding or acquiring a write lock.
446
    pub fn is_locked_exclusive(&self) -> bool {
447
        self.control.forbidden.load(Ordering::Relaxed)
448
    }
449

            
450
    /// Obtain mutable access to the object without locking.
451
    ///
452
    /// Returns `None` when other clones of this shared mutex exist, since those clones can
453
    /// concurrently hold read or write guards on the shared object.
454
    pub fn get_mut(&mut self) -> Option<&mut T> {
455
        {
456
            let other = self.shared.other.lock().expect("Failed to lock mutex");
457
            if other.iter().flatten().count() != 1 {
458
                return None;
459
            }
460
        }
461

            
462
        // SAFETY: The registration table contains only this instance, so no other handle exists
463
        // (guards borrow their handle, and new clones can only be created from a handle). Holding
464
        // `&mut self` therefore guarantees no guard is alive and none can be created.
465
        #[cfg(not(loom))]
466
        unsafe {
467
            Some(&mut *self.shared.object.get())
468
        }
469
        #[cfg(loom)]
470
        unsafe {
471
            Some(self.shared.object.get_mut().with(|ptr| &mut *ptr))
472
        }
473
    }
474
}
475

            
476
impl<T: Debug> Debug for BfSharedMutex<T> {
477
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
478
        // Recover from poisoning so that formatting (often invoked from logging or while
479
        // already panicking) never panics itself.
480
        let other = self
481
            .shared
482
            .other
483
            .lock()
484
            .unwrap_or_else(std::sync::PoisonError::into_inner);
485

            
486
        f.debug_map()
487
            .entry(&"busy", &self.control.busy.load(Ordering::Relaxed))
488
            .entry(&"forbidden", &self.control.forbidden.load(Ordering::Relaxed))
489
            .entry(&"index", &self.index)
490
            .entry(&"len(other)", &other.len())
491
            .finish()?;
492

            
493
        writeln!(f)?;
494
        writeln!(f, "other values: [")?;
495
        for control in other.iter().flatten() {
496
            f.debug_map()
497
                .entry(&"busy", &control.busy.load(Ordering::Relaxed))
498
                .entry(&"forbidden", &control.forbidden.load(Ordering::Relaxed))
499
                .finish()?;
500
            writeln!(f)?;
501
        }
502

            
503
        writeln!(f, "]")
504
    }
505
}
506

            
507
/// A global shared mutex that can be used to protect global data.
508
///
509
/// # Details
510
///
511
/// This is a wrapper around `BfSharedMutex` that provides a global instance
512
/// that can be used to protect global data. Must be cloned to obtain mutable
513
/// access.
514
pub struct GlobalBfSharedMutex<T> {
515
    /// The shared mutex that is used to protect the global data.
516
    shared_mutex: BfSharedMutex<T>,
517
}
518

            
519
impl<T> GlobalBfSharedMutex<T> {
520
    /// Constructs a new global shared mutex for protecting access to the given object.
521
1764
    pub fn new(object: T) -> Self {
522
1764
        Self {
523
1764
            shared_mutex: BfSharedMutex::new(object),
524
1764
        }
525
1764
    }
526

            
527
    /// Returns a clone of the global shared mutex, which allows writing and reading.
528
1768
    pub fn share(&self) -> BfSharedMutex<T> {
529
1768
        self.shared_mutex.clone()
530
1768
    }
531
}
532

            
533
// SAFETY: Moving the global handle to another thread only moves the inner `BfSharedMutex`,
534
// which is itself `Send` for `T: Send + Sync`.
535
unsafe impl<T: Send + Sync> Send for GlobalBfSharedMutex<T> {}
536

            
537
// SAFETY: Multiple threads holding &GlobalBfSharedMutex<T> can call share() concurrently;
538
// share() only clones the inner mutex, which serialises registration under its own lock.
539
unsafe impl<T: Send + Sync> Sync for GlobalBfSharedMutex<T> {}
540

            
541
#[cfg(test)]
542
mod tests {
543
    use std::hint::black_box;
544

            
545
    use rand::RngExt;
546

            
547
    use merc_utilities::random_test_threads;
548
    use merc_utilities::test_threads;
549

            
550
    use super::BfSharedMutex;
551

            
552
    // These are just simple tests.
553
    #[test]
554
    #[cfg_attr(miri, ignore)]
555
1
    fn test_random_bf_shared_mutex_exclusive() {
556
1
        let shared_number = BfSharedMutex::new(5);
557
1
        let num_iterations = 500;
558
1
        let num_threads = 3;
559

            
560
1
        test_threads(
561
1
            num_threads,
562
3
            || shared_number.clone(),
563
3
            move |number| {
564
1500
                for _ in 0..num_iterations {
565
1500
                    *number.write().unwrap() += 5;
566
1500
                }
567
3
            },
568
        );
569

            
570
1
        assert_eq!(*shared_number.write().unwrap(), num_threads * num_iterations * 5 + 5);
571
1
    }
572

            
573
    #[test]
574
    #[cfg_attr(miri, ignore)]
575
1
    fn test_random_bf_shared_mutex() {
576
1
        let shared_vector = BfSharedMutex::new(vec![]);
577

            
578
1
        let num_threads = 20;
579
1
        let num_iterations = 5000;
580

            
581
1
        random_test_threads(
582
1
            num_iterations,
583
1
            num_threads,
584
20
            || shared_vector.clone(),
585
100000
            |rng, shared_vector| {
586
100000
                if rng.random_bool(0.95) {
587
                    // Read a random index.
588
94943
                    let read = shared_vector.read().unwrap();
589
94943
                    if !read.is_empty() {
590
94890
                        let index = rng.random_range(0..read.len());
591
94890
                        assert_eq!(*black_box(&read[index]), 5);
592
53
                    }
593
5057
                } else {
594
5057
                    // Add a new vector element.
595
5057
                    shared_vector.write().unwrap().push(5);
596
5057
                }
597
100000
            },
598
        );
599
1
    }
600

            
601
    /// A `try_write` that observes the inner registration mutex already locked must report
602
    /// failure instead of handing out a second exclusive reference.
603
    #[test]
604
    #[cfg_attr(miri, ignore)]
605
1
    fn test_bf_shared_mutex_try_write_fails_when_locked() {
606
1
        let shared_mutex = BfSharedMutex::new(0);
607
1
        let other = shared_mutex.clone();
608

            
609
1
        let guard = shared_mutex.write().unwrap();
610

            
611
        // While the write guard holds the inner mutex, no clone can acquire it.
612
1
        assert!(other.try_write().unwrap().is_none());
613
1
        assert!(shared_mutex.try_write().unwrap().is_none());
614

            
615
1
        drop(guard);
616

            
617
        // Once released, a non-blocking write succeeds again.
618
1
        let mut guard = other
619
1
            .try_write()
620
1
            .unwrap()
621
1
            .expect("try_write should succeed after unlock");
622
1
        *guard += 1;
623
1
        drop(guard);
624

            
625
1
        assert_eq!(*shared_mutex.read().unwrap(), 1);
626
1
    }
627

            
628
    /// Many threads racing through `try_write` must never lose an increment: each successful
629
    /// attempt is guaranteed exclusive access, so retrying until success yields an exact count.
630
    #[test]
631
    #[cfg_attr(miri, ignore)]
632
1
    fn test_concurrent_bf_shared_mutex_try_write() {
633
1
        let shared_number = BfSharedMutex::new(0);
634
1
        let num_iterations = 500;
635
1
        let num_threads = 4;
636

            
637
1
        test_threads(
638
1
            num_threads,
639
4
            || shared_number.clone(),
640
4
            move |number| {
641
4
                for _ in 0..num_iterations {
642
                    // Retry until exclusive access is granted; a contended attempt returns None.
643
                    loop {
644
48939
                        if let Some(mut guard) = number.try_write().unwrap() {
645
2000
                            *guard += 1;
646
2000
                            break;
647
46939
                        }
648
                    }
649
                }
650
4
            },
651
        );
652

            
653
1
        assert_eq!(*shared_number.write().unwrap(), num_threads * num_iterations);
654
1
    }
655

            
656
    #[test]
657
    #[cfg(loom)]
658
    fn test_loom_bf_shared_mutex() {
659
        let mut builder = loom::model::Builder::new();
660
        // A bound of at least 2 is needed to find the missing fence.
661
        builder.preemption_bound = Some(2);
662

            
663
        builder.check(|| {
664
            let shared_mutex = BfSharedMutex::new(false);
665

            
666
            let threads: Vec<_> = (0..3)
667
                .map(|_| {
668
                    let shared_mutex = shared_mutex.clone();
669
                    loom::thread::spawn(move || {
670
                        // Just perform some operations on the shared mutex.
671
                        let result = *shared_mutex.read().unwrap();
672

            
673
                        *shared_mutex.write().unwrap() = !result;
674
                    })
675
                })
676
                .collect();
677

            
678
            for th in threads {
679
                th.join().unwrap();
680
            }
681
        });
682
    }
683

            
684
    #[test]
685
    #[cfg(loom)]
686
    fn test_loom_bf_shared_mutex_try_write() {
687
        let mut builder = loom::model::Builder::new();
688
        // A bound of at least 2 is needed to find the missing fence.
689
        builder.preemption_bound = Some(2);
690

            
691
        builder.check(|| {
692
            let shared_mutex = BfSharedMutex::new(0u32);
693

            
694
            let threads: Vec<_> = (0..2)
695
                .map(|_| {
696
                    let shared_mutex = shared_mutex.clone();
697
                    loom::thread::spawn(move || {
698
                        // A non-blocking write attempt either succeeds with exclusive
699
                        // access or reports contention; both branches must be sound.
700
                        if let Some(mut guard) = shared_mutex.try_write().unwrap() {
701
                            *guard += 1;
702
                        }
703

            
704
                        // A subsequent read must always succeed under the protocol.
705
                        let _ = *shared_mutex.read().unwrap();
706
                    })
707
                })
708
                .collect();
709

            
710
            for th in threads {
711
                th.join().unwrap();
712
            }
713
        });
714
    }
715
}