wl_client/proxy/low_level/borrowed.rs
1use {
2 crate::{
3 Libwayland, Queue,
4 builder::prelude::UntypedOwnedProxy,
5 ffi::{wl_argument, wl_interface, wl_proxy},
6 proxy::low_level::{check_dispatching_proxy, check_new_proxy},
7 utils::sync_ptr::SyncPtr,
8 },
9 parking_lot::{RwLock, RwLockReadGuard},
10 std::{
11 ptr::{self, NonNull},
12 sync::atomic::{AtomicPtr, AtomicU32, Ordering::Relaxed},
13 },
14};
15
16#[cfg(test)]
17mod tests;
18
19/// A borrowed `wl_proxy` pointer.
20///
21/// Most of the time you will not work with this type directly and instead use one of
22/// the `*Ref` types generated by `wl-client-builder`.
23///
24/// This is a thin wrapper around a `wl_proxy` pointer plus a read-write lock. The
25/// contained proxy pointer is at all times either null or a valid pointer.
26///
27/// You can access the pointer by calling [`UntypedBorrowedProxy::wl_proxy`], however,
28/// this does not prevent concurrent destruction. That is, you might check that the
29/// returned pointer is not null and another thread might invalidate the pointer at the
30/// same time.
31///
32/// To prevent this, you can acquire the read lock mentioned above by calling
33/// [`UntypedBorrowedProxy::lock`]:
34///
35/// ```
36/// # use std::ptr::NonNull;
37/// # use wl_client::builder::prelude::{wl_proxy, UntypedBorrowedProxy};
38/// #
39/// fn pass_proxy_pointer_to_external_function(proxy: &UntypedBorrowedProxy) {
40/// let lock = proxy.lock();
41/// // SAFETY: The lock ensures that the proxy stays valid if it is not null.
42/// unsafe {
43/// external_function(lock.wl_proxy());
44/// }
45/// }
46///
47/// /// # Safety
48/// ///
49/// /// The proxy must be valid or null.
50/// unsafe fn external_function(proxy: Option<NonNull<wl_proxy>>) {
51/// todo!();
52/// }
53/// ```
54pub struct UntypedBorrowedProxy {
55 /// The proxy wrapped by this object. The value must only be modified while holding
56 /// the write lock. If immutable is true, then the value must not be modified.
57 pub(super) proxy: AtomicPtr<wl_proxy>,
58 pub(super) lock: RwLock<()>,
59 pub(super) libwayland: &'static Libwayland,
60 id: AtomicU32,
61 immutable: bool,
62}
63
64/// A transparent wrapper around [`UntypedBorrowedProxy`].
65///
66/// # Safety
67///
68/// This type must be a transparent wrapper around [`UntypedBorrowedProxy`].
69pub unsafe trait UntypedBorrowedProxyWrapper: Sized {}
70
71// SAFETY: Self is literally UntypedBorrowedProxy
72unsafe impl UntypedBorrowedProxyWrapper for UntypedBorrowedProxy {}
73
74const MIN_SERVER_ID: u32 = 0xff000000;
75const LAST_CLIENT_ID: u32 = MIN_SERVER_ID - 1;
76
77/// A lock that ensures that a [`UntypedBorrowedProxy`] is not changed.
78///
79/// You can acquire it by calling [`proxy::lock`](super::super::lock).
80///
81/// This lock can be acquired multiple times across multiple threads.
82pub struct BorrowedProxyLock<'a> {
83 _lock: Option<RwLockReadGuard<'a, ()>>,
84 proxy: SyncPtr<wl_proxy>,
85}
86
87impl BorrowedProxyLock<'_> {
88 /// Returns the `wl_proxy` pointer or `None` if the proxy is destroyed.
89 ///
90 /// If this function returns a pointer, then the pointer will remain valid while the
91 /// lock is being held.
92 ///
93 /// # Example
94 ///
95 /// ```
96 /// # use wl_client::{proxy, Libwayland};
97 /// use wl_client::test_protocols::core::wl_display::WlDisplay;
98 /// #
99 /// let lib = Libwayland::open().unwrap();
100 /// let con = lib.connect_to_default_display().unwrap();
101 /// let queue = con.create_queue(c"queue name");
102 /// let display: WlDisplay = queue.display();
103 ///
104 /// let sync = display.sync();
105 /// let lock = proxy::lock(&*sync);
106 /// assert!(lock.wl_proxy().is_some());
107 ///
108 /// // This would deadlock.
109 /// // proxy::destroy(&sync);
110 /// ```
111 pub fn wl_proxy(&self) -> Option<NonNull<wl_proxy>> {
112 NonNull::new(self.proxy.0)
113 }
114}
115
116impl UntypedBorrowedProxy {
117 /// Creates a new [`UntypedBorrowedProxy`] from an immutable pointer.
118 ///
119 /// The pointer contained in the new object stays valid for the lifetime of the
120 /// object.
121 ///
122 /// # Safety
123 ///
124 /// - `proxy` must be a valid pointer.
125 /// - `proxy` must stay valid for the lifetime of this object.
126 pub unsafe fn new_immutable(libwayland: &'static Libwayland, proxy: NonNull<wl_proxy>) -> Self {
127 // SAFETY: The requirement is forwarded to the caller.
128 unsafe { Self::new(libwayland, proxy, true) }
129 }
130
131 /// Creates a new [`UntypedBorrowedProxy`].
132 ///
133 /// # Safety
134 ///
135 /// - `proxy` must be a valid pointer.
136 /// - The proxy must be set to null before being invalidated.
137 pub(crate) unsafe fn new_internal(
138 libwayland: &'static Libwayland,
139 proxy: NonNull<wl_proxy>,
140 ) -> Self {
141 // SAFETY: The requirement is forwarded to the caller.
142 unsafe { Self::new(libwayland, proxy, false) }
143 }
144
145 /// Creates a new [`UntypedBorrowedProxy`].
146 ///
147 /// # Safety
148 ///
149 /// - The proxy must either be null or a valid pointer.
150 /// - This property must hold for the lifetime of this object.
151 unsafe fn new(
152 libwayland: &'static Libwayland,
153 proxy: NonNull<wl_proxy>,
154 immutable: bool,
155 ) -> Self {
156 Self {
157 proxy: AtomicPtr::new(proxy.as_ptr()),
158 lock: RwLock::new(()),
159 id: AtomicU32::new(LAST_CLIENT_ID),
160 libwayland,
161 immutable,
162 }
163 }
164
165 /// Locks the `wl_proxy` pointer in this object.
166 ///
167 /// While the pointer is locked, it does not change. Given the invariants of this type,
168 /// this implies that the pointer will either stay the same, valid pointer, or the
169 /// pointer will be the null pointer.
170 ///
171 /// This lock can be acquired multiple times concurrently.
172 ///
173 /// Trying to destroy the proxy while holding a lock will deadlock.
174 #[inline]
175 pub fn lock(&self) -> BorrowedProxyLock<'_> {
176 let lock = match self.immutable {
177 true => None,
178 false => Some(self.lock.read_recursive()),
179 };
180 // SAFETY: If the pointer is not immutable, then we're holding a read lock.
181 BorrowedProxyLock {
182 _lock: lock,
183 proxy: SyncPtr(self.proxy.load(Relaxed)),
184 }
185 }
186
187 /// Retrieve the `wl_proxy` pointer backing this object.
188 ///
189 /// In a multi-threaded application, the pointer can get invalidated at any time. To
190 /// prevent this, you might want to use [`UntypedBorrowedProxy::lock`] instead.
191 #[inline]
192 pub fn wl_proxy(&self) -> Option<NonNull<wl_proxy>> {
193 NonNull::new(self.proxy.load(Relaxed))
194 }
195
196 /// Returns the wayland object ID of this proxy.
197 ///
198 /// If the proxy has already been destroyed, this function returns either the original
199 /// ID or 0.
200 #[inline]
201 pub fn id(&self) -> u32 {
202 let mut id = self.id.load(Relaxed);
203 if id == LAST_CLIENT_ID {
204 id = self.id_slow();
205 self.id.store(id, Relaxed);
206 }
207 id
208 }
209
210 #[cold]
211 fn id_slow(&self) -> u32 {
212 let lock = self.lock();
213 if let Some(proxy) = lock.wl_proxy() {
214 // SAFETY: Since proxy is not null, the invariants guarantee that proxy is
215 // valid.
216 unsafe { self.libwayland.wl_proxy_get_id(proxy.as_ptr()) }
217 } else {
218 0
219 }
220 }
221
222 /// Sends a request on this proxy.
223 ///
224 /// This function cannot be used if the request creates a new object. Use
225 /// [`UntypedBorrowedProxy::send_constructor`] for that purpose.
226 ///
227 /// # Panic
228 ///
229 /// Panics if this proxy has already been destroyed.
230 ///
231 /// # Safety
232 ///
233 /// - `opcode` must be a valid request opcode for the interface of the proxy.
234 /// - `args` must conform to the interface + opcode of the proxy.
235 /// - `args` must not contain any `new_id` element.
236 #[inline]
237 pub unsafe fn send_request(&self, opcode: u32, args: &mut [wl_argument]) {
238 let lock = self.lock();
239 let proxy = check_dispatching_proxy(lock.wl_proxy());
240 // SAFETY: - we've checked that proxy is not null, therefore the invariants
241 // guarantee that it is a valid pointer
242 // - the opcode and args requirements are forwarded to the caller
243 // - in particular, interface is null and the args does not contain a
244 // new_id element
245 // - flags does not contain WL_MARSHAL_FLAG_DESTROY
246 unsafe {
247 self.libwayland.wl_proxy_marshal_array_flags(
248 proxy.as_ptr(),
249 opcode,
250 ptr::null(),
251 0,
252 0,
253 args.as_mut_ptr(),
254 );
255 }
256 }
257
258 /// Creates a new object by sending a request on this proxy.
259 ///
260 /// This function can only be used if the request creates a new object. Use
261 /// [`Self::send_request`] otherwise.
262 ///
263 /// The new object will be attached to the given queue.
264 ///
265 /// # Panic
266 ///
267 /// This function panics if
268 ///
269 /// - this proxy has already been destroyed, or
270 /// - the queue and this proxy don't belong to the same display.
271 ///
272 /// # Safety
273 ///
274 /// - `opcode` must be a valid request opcode for the interface of the proxy.
275 /// - `args` must conform to the interface + opcode of the proxy.
276 /// - `args` must contain exactly one `new_id` element.
277 /// - `interface` must be a valid `wl_interface`.
278 pub unsafe fn send_constructor(
279 &self,
280 queue: &Queue,
281 opcode: u32,
282 args: &mut [wl_argument],
283 interface: &'static wl_interface,
284 version: Option<u32>,
285 ) -> UntypedOwnedProxy {
286 let lib = self.libwayland;
287 let lock = self.lock();
288 let mut proxy = check_dispatching_proxy(lock.wl_proxy());
289 // SAFETY: - we've checked that proxy is not null, therefore the invariants
290 // guarantee that it is a valid pointer
291 let version =
292 version.unwrap_or_else(|| unsafe { lib.wl_proxy_get_version(proxy.as_ptr()) });
293 // SAFETY: - we've checked that proxy is not null, therefore the invariants
294 // guarantee that it is a valid pointer
295 let parent_queue = unsafe { lib.wl_proxy_get_queue(proxy.as_ptr()) };
296 let need_wrapper = parent_queue != queue.wl_event_queue().as_ptr();
297 if need_wrapper {
298 // SAFETY: - We've verified that the proxy is not null. UntypedBorrowedProxy
299 // requires that the pointer is valid in this case.
300 let display = unsafe { self.libwayland.wl_proxy_get_display(proxy.as_ptr()) };
301 if display != queue.connection().wl_display().as_ptr() {
302 panic!("queue does not belong to same connection");
303 }
304 // SAFETY: - we've checked that proxy is not null, therefore the invariants
305 // guarantee that it is a valid pointer
306 let proxy_ptr = unsafe { lib.wl_proxy_create_wrapper(proxy.as_ptr().cast()).cast() };
307 proxy = check_dispatching_proxy(NonNull::new(proxy_ptr));
308 // SAFETY: - we've checked that proxy is not null, therefore the invariants
309 // guarantee that it is a valid pointer
310 // - the invariants of queue guarantee that queue the wl_event_queue
311 // is valid
312 // - we've checked above that the display of the queue and the
313 // display of the proxy are the same
314 // - we will destroy the wrapper below
315 unsafe {
316 lib.wl_proxy_set_queue(proxy.as_ptr(), queue.wl_event_queue().as_ptr());
317 }
318 }
319 // SAFETY: - we've checked that proxy is not null, therefore the invariants
320 // guarantee that it is a valid pointer
321 // - the opcode, args, and interface requirements are forwarded to the
322 // caller
323 // - in particular, interface is a valid wl_interface and args contains
324 // exactly one new_id element
325 // - flags does not contain WL_MARSHAL_FLAG_DESTROY
326 let new_proxy = unsafe {
327 lib.wl_proxy_marshal_array_flags(
328 proxy.as_ptr(),
329 opcode,
330 interface,
331 version,
332 0,
333 args.as_mut_ptr(),
334 )
335 };
336 if need_wrapper {
337 // SAFETY: Since need_wrapper is true, wl_proxy_create_wrapper is the return
338 // value of wl_proxy_create_wrapper above.
339 unsafe {
340 lib.wl_proxy_wrapper_destroy(proxy.as_ptr().cast());
341 }
342 }
343 let new_proxy = check_new_proxy(new_proxy);
344 // SAFETY: - we've just checked that new_proxy is not null
345 // - since new_proxy was returned by wl_proxy_marshal_array_flags its a
346 // valid, plain proxy that we have ownership of
347 // - we hand over ownership to the UntypedOwnedProxy
348 // - we made sure above that new_proxy would be created in `queue`
349 // - the object was created with the interface `interface`
350 // - since it was just created, it does not have an event handler assigned
351 unsafe { UntypedOwnedProxy::from_plain_wl_proxy(queue, new_proxy, interface) }
352 }
353
354 /// Returns the version of this proxy object.
355 ///
356 /// The version of the display object is always 0.
357 ///
358 /// # Panic
359 ///
360 /// Panics if the proxy is already destroyed.
361 pub fn version(&self) -> u32 {
362 let lock = self.lock();
363 let proxy = check_dispatching_proxy(lock.wl_proxy());
364 // SAFETY: - we've checked that proxy is not null, therefore the invariants
365 // guarantee that it is a valid pointer
366 unsafe { self.libwayland.wl_proxy_get_version(proxy.as_ptr()) }
367 }
368}
369
370impl PartialEq for UntypedBorrowedProxy {
371 fn eq(&self, other: &Self) -> bool {
372 self.wl_proxy() == other.wl_proxy()
373 }
374}
375
376impl Eq for UntypedBorrowedProxy {}
377
378impl PartialEq<UntypedOwnedProxy> for UntypedBorrowedProxy {
379 fn eq(&self, other: &UntypedOwnedProxy) -> bool {
380 self.wl_proxy() == other.wl_proxy()
381 }
382}
383
384impl PartialEq<UntypedBorrowedProxy> for UntypedOwnedProxy {
385 fn eq(&self, other: &UntypedBorrowedProxy) -> bool {
386 self.wl_proxy() == other.wl_proxy()
387 }
388}