Skip to content

Commit

Permalink
backend: Add destroy_object method
Browse files Browse the repository at this point in the history
This is needed for implementing the `wl_fixes` protocol. It should also
make it possible to handle the requirement in
`zwlr_output_configuration_v1::destroy` to destroy the
`wlr_output_configuration_head`.
  • Loading branch information
ids1024 committed Dec 24, 2024
1 parent c23ed41 commit 10b6ff0
Show file tree
Hide file tree
Showing 8 changed files with 119 additions and 27 deletions.
3 changes: 3 additions & 0 deletions wayland-backend/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## Unreleased

### Additions
- backend: Added a `destroy_object` method

### Bugfixes

- backend/rs: Prevent a potential deadlock during client cleanup
Expand Down
11 changes: 11 additions & 0 deletions wayland-backend/src/client_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,17 @@ impl Backend {
self.backend.info(id)
}

/// Destroy an object
///
/// For most protocols, this is handled automatically when a destructor
/// message is sent or received.
///
/// This corresponds to `wl_proxy_destroy` in the C API. Or a `_destroy`
/// method generated for an object without a destructor request.
pub fn destroy_object(&self, id: &ObjectId) -> Result<(), InvalidId> {
self.backend.destroy_object(id)
}

/// Sends a request to the server
///
/// Returns an error if the sender ID of the provided message is no longer valid.
Expand Down
13 changes: 13 additions & 0 deletions wayland-backend/src/rs/client_impl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,19 @@ impl InnerBackend {
ObjectId { id: InnerObjectId { serial: 0, id: 0, interface: &ANONYMOUS_INTERFACE } }
}

pub fn destroy_object(&self, id: &ObjectId) -> Result<(), InvalidId> {
let mut guard = self.state.lock_protocol();
let object = guard.get_object(id.id.clone())?;
guard
.map
.with(id.id.id, |obj| {
obj.data.client_destroyed = true;
})
.unwrap();
object.data.user_data.destroyed(id.clone());
Ok(())
}

pub fn send_request(
&self,
Message { sender_id: ObjectId { id }, opcode, args }: Message<ObjectId, RawFd>,
Expand Down
14 changes: 12 additions & 2 deletions wayland-backend/src/rs/server_impl/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,17 @@ impl<D> Client<D> {
InnerObjectId { id, serial, client_id: self.id.clone(), interface }
}

pub(crate) fn destroy_object(
&mut self,
id: InnerObjectId,
pending_destructors: &mut Vec<super::handle::PendingDestructor<D>>,
) -> Result<(), InvalidId> {
let object = self.get_object(id.clone())?;
pending_destructors.push((object.data.user_data.clone(), self.id.clone(), id.clone()));
self.send_delete_id(id.clone());
Ok(())
}

pub(crate) fn object_info(&self, id: InnerObjectId) -> Result<ObjectInfo, InvalidId> {
let object = self.get_object(id.clone())?;
Ok(ObjectInfo { id: id.id, interface: object.interface, version: object.version })
Expand Down Expand Up @@ -201,7 +212,6 @@ impl<D> Client<D> {

// Handle destruction if relevant
if message_desc.is_destructor {
self.map.remove(object_id.id.id);
if let Some(vec) = pending_destructors {
vec.push((object.data.user_data.clone(), self.id.clone(), object_id.id.clone()));
}
Expand Down Expand Up @@ -378,7 +388,7 @@ impl<D> Client<D> {
}
}

fn get_object(&self, id: InnerObjectId) -> Result<Object<Data<D>>, InvalidId> {
pub(crate) fn get_object(&self, id: InnerObjectId) -> Result<Object<Data<D>>, InvalidId> {
let object = self.map.find(id.id).ok_or(InvalidId)?;
if object.data.serial != id.serial {
return Err(InvalidId);
Expand Down
9 changes: 9 additions & 0 deletions wayland-backend/src/rs/server_impl/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,15 @@ impl InnerHandle {
Ok(ObjectId { id: client.create_object(interface, version, data) })
}

pub fn destroy_object<D: 'static>(&self, id: &ObjectId) -> Result<(), InvalidId> {
let mut state = self.state.lock().unwrap();
let state = (&mut *state as &mut dyn ErasedState)
.downcast_mut::<State<D>>()
.expect("Wrong type parameter passed to Handle::destroy_object().");
let client = state.clients.get_client_mut(id.id.client_id.clone())?;
client.destroy_object(id.id.clone(), &mut state.pending_destructors)
}

pub fn null_id() -> ObjectId {
ObjectId {
id: InnerObjectId {
Expand Down
15 changes: 15 additions & 0 deletions wayland-backend/src/server_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,21 @@ impl Handle {
self.handle.create_object(client_id.id, interface, version, data)
}

/// Destroy an object
///
/// For most protocols, this is handled automatically when a destructor
/// message is sent or received.
///
/// This corresponds to `wl_resource_destroy` in the C API.
///
/// # Panics
///
/// This method will panic if the type parameter `D` is not same to the same type as the
/// one the backend was initialized with.
pub fn destroy_object<D: 'static>(&self, id: &ObjectId) -> Result<(), InvalidId> {
self.handle.destroy_object::<D>(id)
}

/// Send an event to the client
///
/// Returns an error if the sender ID of the provided message is no longer valid.
Expand Down
63 changes: 38 additions & 25 deletions wayland-backend/src/sys/client_impl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,43 @@ impl InnerBackend {
}
}

fn destroy_object_inner(&self, guard: &mut MutexGuard<ConnectionState>, id: &ObjectId) {
if let Some(ref alive) = id.id.alive {
let udata = unsafe {
Box::from_raw(ffi_dispatch!(
wayland_client_handle(),
wl_proxy_get_user_data,
id.id.ptr
) as *mut ProxyUserData)
};
unsafe {
ffi_dispatch!(
wayland_client_handle(),
wl_proxy_set_user_data,
id.id.ptr,
std::ptr::null_mut()
);
}
alive.store(false, Ordering::Release);
udata.data.destroyed(id.clone());
}

guard.known_proxies.remove(&id.id.ptr);

unsafe {
ffi_dispatch!(wayland_client_handle(), wl_proxy_destroy, id.id.ptr);
}
}

pub fn destroy_object(&self, id: &ObjectId) -> Result<(), InvalidId> {
if !id.id.alive.as_ref().map(|a| a.load(Ordering::Acquire)).unwrap_or(false) {
return Err(InvalidId);
}

self.destroy_object_inner(&mut self.lock_state(), id);
Ok(())
}

pub fn send_request(
&self,
Message { sender_id: ObjectId { id }, opcode, args }: Message<ObjectId, RawFd>,
Expand Down Expand Up @@ -685,31 +722,7 @@ impl InnerBackend {
};

if message_desc.is_destructor {
if let Some(ref alive) = id.alive {
let udata = unsafe {
Box::from_raw(ffi_dispatch!(
wayland_client_handle(),
wl_proxy_get_user_data,
id.ptr
) as *mut ProxyUserData)
};
unsafe {
ffi_dispatch!(
wayland_client_handle(),
wl_proxy_set_user_data,
id.ptr,
std::ptr::null_mut()
);
}
alive.store(false, Ordering::Release);
udata.data.destroyed(ObjectId { id: id.clone() });
}

guard.known_proxies.remove(&id.ptr);

unsafe {
ffi_dispatch!(wayland_client_handle(), wl_proxy_destroy, id.ptr);
}
self.destroy_object_inner(&mut guard, &ObjectId { id })
}

Ok(child_id)
Expand Down
18 changes: 18 additions & 0 deletions wayland-backend/src/sys/server_impl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -564,6 +564,24 @@ impl InnerHandle {
Ok(ObjectId { id: unsafe { init_resource(resource, interface, Some(data)).0 } })
}

pub fn destroy_object<D: 'static>(&self, id: &ObjectId) -> Result<(), InvalidId> {
let mut state = self.state.lock().unwrap();
// Keep this guard alive while the code is run to protect the C state
let state = (&mut *state as &mut dyn ErasedState)
.downcast_mut::<State<D>>()
.expect("Wrong type parameter passed to Handle::destroy_object().");

if !id.id.alive.load(Ordering::Acquire) {
return Err(InvalidId);
}

PENDING_DESTRUCTORS.set(&(&mut state.pending_destructors as *mut _ as *mut _), || unsafe {
ffi_dispatch!(wayland_server_handle(), wl_resource_destroy, id.id.ptr);
});

Ok(())
}

pub fn null_id() -> ObjectId {
ObjectId {
id: InnerObjectId {
Expand Down

0 comments on commit 10b6ff0

Please sign in to comment.