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}