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}