fuchsia_sync/
mutex.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
// Copyright 2023 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use zx::sys;

extern "C" {
    fn sync_mutex_lock(lock: *const sys::zx_futex_t);
    fn sync_mutex_trylock(lock: *const sys::zx_futex_t) -> sys::zx_status_t;
    fn sync_mutex_unlock(lock: *const sys::zx_futex_t);
}

// See SYNC_MUTEX_INIT in lib/sync/mutex.h
const SYNC_MUTEX_INIT: i32 = 0;

#[repr(transparent)]
pub struct RawSyncMutex(sys::zx_futex_t);

impl RawSyncMutex {
    #[inline]
    fn as_futex_ptr(&self) -> *const sys::zx_futex_t {
        std::ptr::addr_of!(self.0)
    }
}

// SAFETY: This trait requires that "[i]mplementations of this trait must ensure
// that the mutex is actually exclusive: a lock can't be acquired while the mutex
// is already locked." This guarantee is provided by libsync's APIs.
unsafe impl lock_api::RawMutex for RawSyncMutex {
    const INIT: RawSyncMutex = RawSyncMutex(sys::zx_futex_t::new(SYNC_MUTEX_INIT));

    // libsync does not require the lock / unlock operations to happen on the same thread.
    type GuardMarker = lock_api::GuardSend;

    #[inline]
    fn lock(&self) {
        // SAFETY: This call requires we pass a non-null pointer to a valid futex.
        // This is guaranteed by using `self` through a shared reference.
        unsafe {
            sync_mutex_lock(self.as_futex_ptr());
        }
    }

    #[inline]
    fn try_lock(&self) -> bool {
        // SAFETY: This call requires we pass a non-null pointer to a valid futex.
        // This is guaranteed by using `self` through a shared reference.
        unsafe { sync_mutex_trylock(self.as_futex_ptr()) == sys::ZX_OK }
    }

    #[inline]
    unsafe fn unlock(&self) {
        sync_mutex_unlock(self.as_futex_ptr())
    }
}

pub type Mutex<T> = lock_api::Mutex<RawSyncMutex, T>;
pub type MutexGuard<'a, T> = lock_api::MutexGuard<'a, RawSyncMutex, T>;
pub type MappedMutexGuard<'a, T> = lock_api::MappedMutexGuard<'a, RawSyncMutex, T>;

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_lock_and_unlock() {
        let value = Mutex::<u32>::new(5);
        let mut guard = value.lock();
        assert_eq!(*guard, 5);
        *guard = 6;
        assert_eq!(*guard, 6);
        std::mem::drop(guard);
    }

    #[test]
    fn test_try_lock() {
        let value = Mutex::<u32>::new(5);
        let _guard = value.lock();
        assert!(value.try_lock().is_none());
    }
}