fuchsia_sync/
mutex.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use zx::sys;
6
7extern "C" {
8    fn sync_mutex_lock(lock: *const sys::zx_futex_t);
9    fn sync_mutex_trylock(lock: *const sys::zx_futex_t) -> sys::zx_status_t;
10    fn sync_mutex_unlock(lock: *const sys::zx_futex_t);
11}
12
13// See SYNC_MUTEX_INIT in lib/sync/mutex.h
14const SYNC_MUTEX_INIT: i32 = 0;
15
16#[repr(transparent)]
17pub struct RawSyncMutex(sys::zx_futex_t);
18
19impl RawSyncMutex {
20    #[inline]
21    fn as_futex_ptr(&self) -> *const sys::zx_futex_t {
22        std::ptr::addr_of!(self.0)
23    }
24}
25
26// SAFETY: This trait requires that "[i]mplementations of this trait must ensure
27// that the mutex is actually exclusive: a lock can't be acquired while the mutex
28// is already locked." This guarantee is provided by libsync's APIs.
29unsafe impl lock_api::RawMutex for RawSyncMutex {
30    const INIT: RawSyncMutex = RawSyncMutex(sys::zx_futex_t::new(SYNC_MUTEX_INIT));
31
32    // libsync does not require the lock / unlock operations to happen on the same thread.
33    // However, we set this to no send to catch mistakes where folks accidentally hold a lock across
34    // an async await, which is often not intentional behavior and can lead to a deadlock. If
35    // sufficient need is required, this may be changed back to `lock_api::GuardSend`.
36    type GuardMarker = lock_api::GuardNoSend;
37
38    #[inline]
39    fn lock(&self) {
40        // SAFETY: This call requires we pass a non-null pointer to a valid futex.
41        // This is guaranteed by using `self` through a shared reference.
42        unsafe {
43            sync_mutex_lock(self.as_futex_ptr());
44        }
45    }
46
47    #[inline]
48    fn try_lock(&self) -> bool {
49        // SAFETY: This call requires we pass a non-null pointer to a valid futex.
50        // This is guaranteed by using `self` through a shared reference.
51        unsafe { sync_mutex_trylock(self.as_futex_ptr()) == sys::ZX_OK }
52    }
53
54    #[inline]
55    unsafe fn unlock(&self) {
56        sync_mutex_unlock(self.as_futex_ptr())
57    }
58}
59
60pub type Mutex<T> = lock_api::Mutex<RawSyncMutex, T>;
61pub type MutexGuard<'a, T> = lock_api::MutexGuard<'a, RawSyncMutex, T>;
62pub type MappedMutexGuard<'a, T> = lock_api::MappedMutexGuard<'a, RawSyncMutex, T>;
63
64#[cfg(test)]
65mod test {
66    use super::*;
67
68    #[test]
69    fn test_lock_and_unlock() {
70        let value = Mutex::<u32>::new(5);
71        let mut guard = value.lock();
72        assert_eq!(*guard, 5);
73        *guard = 6;
74        assert_eq!(*guard, 6);
75        std::mem::drop(guard);
76    }
77
78    #[test]
79    fn test_try_lock() {
80        let value = Mutex::<u32>::new(5);
81        let _guard = value.lock();
82        assert!(value.try_lock().is_none());
83    }
84}