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.
45use zx::sys;
67extern "C" {
8fn sync_mutex_lock(lock: *const sys::zx_futex_t);
9fn sync_mutex_trylock(lock: *const sys::zx_futex_t) -> sys::zx_status_t;
10fn sync_mutex_unlock(lock: *const sys::zx_futex_t);
11}
1213// See SYNC_MUTEX_INIT in lib/sync/mutex.h
14const SYNC_MUTEX_INIT: i32 = 0;
1516#[repr(transparent)]
17pub struct RawSyncMutex(sys::zx_futex_t);
1819impl RawSyncMutex {
20#[inline]
21fn as_futex_ptr(&self) -> *const sys::zx_futex_t {
22std::ptr::addr_of!(self.0)
23 }
24}
2526// 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 {
30const INIT: RawSyncMutex = RawSyncMutex(sys::zx_futex_t::new(SYNC_MUTEX_INIT));
3132// libsync does not require the lock / unlock operations to happen on the same thread.
33type GuardMarker = lock_api::GuardSend;
3435#[inline]
36fn lock(&self) {
37// SAFETY: This call requires we pass a non-null pointer to a valid futex.
38 // This is guaranteed by using `self` through a shared reference.
39unsafe {
40 sync_mutex_lock(self.as_futex_ptr());
41 }
42 }
4344#[inline]
45fn try_lock(&self) -> bool {
46// SAFETY: This call requires we pass a non-null pointer to a valid futex.
47 // This is guaranteed by using `self` through a shared reference.
48unsafe { sync_mutex_trylock(self.as_futex_ptr()) == sys::ZX_OK }
49 }
5051#[inline]
52unsafe fn unlock(&self) {
53 sync_mutex_unlock(self.as_futex_ptr())
54 }
55}
5657pub type Mutex<T> = lock_api::Mutex<RawSyncMutex, T>;
58pub type MutexGuard<'a, T> = lock_api::MutexGuard<'a, RawSyncMutex, T>;
59pub type MappedMutexGuard<'a, T> = lock_api::MappedMutexGuard<'a, RawSyncMutex, T>;
6061#[cfg(test)]
62mod test {
63use super::*;
6465#[test]
66fn test_lock_and_unlock() {
67let value = Mutex::<u32>::new(5);
68let mut guard = value.lock();
69assert_eq!(*guard, 5);
70*guard = 6;
71assert_eq!(*guard, 6);
72 std::mem::drop(guard);
73 }
7475#[test]
76fn test_try_lock() {
77let value = Mutex::<u32>::new(5);
78let _guard = value.lock();
79assert!(value.try_lock().is_none());
80 }
81}