Skip to main content

mtl_foundation/
lock.rs

1//! Locking types for Foundation.
2//!
3//! Corresponds to `Foundation/NSLock.hpp`.
4//!
5//! # C++ Equivalent
6//!
7//! ```cpp
8//! namespace NS {
9//! template <class _Class, class _Base = class Object>
10//! class Locking : public _Base {
11//! public:
12//!     void lock();
13//!     void unlock();
14//! };
15//!
16//! class Condition : public Locking<Condition> {
17//! public:
18//!     static Condition* alloc();
19//!     Condition*        init();
20//!     void              wait();
21//!     bool              waitUntilDate(Date* pLimit);
22//!     void              signal();
23//!     void              broadcast();
24//! };
25//! }
26//! ```
27
28use std::ffi::c_void;
29use std::ptr::NonNull;
30
31use mtl_sys::{class, msg_send_0, msg_send_1, sel};
32
33use crate::date::Date;
34use crate::object::Referencing;
35
36/// Trait for objects that support locking.
37///
38/// C++ equivalent: `NS::Locking<_Class, _Base>`
39pub trait Locking: Referencing {
40    /// Lock the object.
41    ///
42    /// C++ equivalent: `void lock()`
43    fn lock(&self) {
44        unsafe {
45            msg_send_0::<()>(self.as_ptr(), sel!(lock));
46        }
47    }
48
49    /// Unlock the object.
50    ///
51    /// C++ equivalent: `void unlock()`
52    fn unlock(&self) {
53        unsafe {
54            msg_send_0::<()>(self.as_ptr(), sel!(unlock));
55        }
56    }
57}
58
59/// An Objective-C condition object.
60///
61/// C++ equivalent: `NS::Condition`
62#[repr(transparent)]
63#[derive(Clone)]
64pub struct Condition(NonNull<c_void>);
65
66impl Condition {
67    /// Allocate a new condition.
68    ///
69    /// C++ equivalent: `static Condition* alloc()`
70    #[inline]
71    pub fn alloc() -> Option<Self> {
72        unsafe {
73            let ptr: *mut c_void = msg_send_0(class!(NSCondition).as_ptr(), sel!(alloc));
74            Self::from_ptr(ptr)
75        }
76    }
77
78    /// Initialize an allocated condition.
79    ///
80    /// C++ equivalent: `Condition* init()`
81    #[inline]
82    pub fn init(&self) -> Option<Self> {
83        unsafe {
84            let ptr: *mut c_void = msg_send_0(self.as_ptr(), sel!(init));
85            Self::from_ptr(ptr)
86        }
87    }
88
89    /// Wait on the condition.
90    ///
91    /// C++ equivalent: `void wait()`
92    #[inline]
93    pub fn wait(&self) {
94        unsafe {
95            msg_send_0::<()>(self.as_ptr(), sel!(wait));
96        }
97    }
98
99    /// Wait on the condition until a date.
100    ///
101    /// C++ equivalent: `bool waitUntilDate(Date* pLimit)`
102    #[inline]
103    pub fn wait_until_date(&self, limit: &Date) -> bool {
104        unsafe { msg_send_1(self.as_ptr(), sel!(waitUntilDate:), limit.as_ptr()) }
105    }
106
107    /// Signal one waiting thread.
108    ///
109    /// C++ equivalent: `void signal()`
110    #[inline]
111    pub fn signal(&self) {
112        unsafe {
113            msg_send_0::<()>(self.as_ptr(), sel!(signal));
114        }
115    }
116
117    /// Signal all waiting threads.
118    ///
119    /// C++ equivalent: `void broadcast()`
120    #[inline]
121    pub fn broadcast(&self) {
122        unsafe {
123            msg_send_0::<()>(self.as_ptr(), sel!(broadcast));
124        }
125    }
126
127    /// Create a new condition (convenience method).
128    #[inline]
129    pub fn new() -> Option<Self> {
130        Self::alloc()?.init()
131    }
132
133    /// Create a Condition from a raw pointer.
134    ///
135    /// # Safety
136    ///
137    /// The pointer must be a valid Objective-C NSCondition object.
138    #[inline]
139    pub unsafe fn from_ptr(ptr: *mut c_void) -> Option<Self> {
140        NonNull::new(ptr).map(Self)
141    }
142}
143
144impl Referencing for Condition {
145    #[inline]
146    fn as_ptr(&self) -> *const c_void {
147        self.0.as_ptr()
148    }
149}
150
151impl Locking for Condition {}
152
153unsafe impl Send for Condition {}
154unsafe impl Sync for Condition {}
155
156impl std::fmt::Debug for Condition {
157    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158        f.debug_struct("Condition").field("ptr", &self.0).finish()
159    }
160}
161
162#[cfg(test)]
163mod tests {
164    use super::*;
165
166    #[test]
167    fn test_condition_size() {
168        assert_eq!(
169            std::mem::size_of::<Condition>(),
170            std::mem::size_of::<*mut c_void>()
171        );
172    }
173}