diff --git a/.gitignore b/.gitignore index 9580f4c2..c5c4672b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .* !.gitignore +!/.cargo /target diff --git a/Cargo.lock b/Cargo.lock index 1fecb9e3..aa4408ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -116,6 +116,19 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + [[package]] name = "env_logger" version = "0.9.0" @@ -269,6 +282,7 @@ dependencies = [ "bitflags", "bstr", "byteorder", + "chrono", "env_logger", "futures", "isnt", @@ -351,6 +365,16 @@ dependencies = [ "syn", ] +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.14" @@ -568,6 +592,17 @@ dependencies = [ "syn", ] +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + [[package]] name = "uapi" version = "0.2.4" @@ -608,9 +643,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" +version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" [[package]] name = "winapi" diff --git a/Cargo.toml b/Cargo.toml index e784c551..42891c19 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ panic = "abort" [profile.dev] panic = "abort" +codegen-units = 1 [dependencies] uapi = "0.2.4" @@ -31,6 +32,7 @@ renderdoc = "0.10.1" smallvec = { version = "1.8.0", features = ["const_generics", "const_new", "union"] } backtrace = "0.3.64" byteorder = "1.4.3" +chrono = "0.4.19" [build-dependencies] repc = "0.1.1" @@ -39,3 +41,6 @@ bstr = "0.2.17" [profile.dev.build-override] opt-level = 3 + +[features] +rc_tracking = [] diff --git a/src/backend.rs b/src/backend.rs index 79c507bc..c3b2148d 100644 --- a/src/backend.rs +++ b/src/backend.rs @@ -1,4 +1,5 @@ use crate::fixed::Fixed; +use std::fmt::Debug; use std::rc::Rc; linear_ids!(OutputIds, OutputId); diff --git a/src/bugs.rs b/src/bugs.rs index ad6ec33d..fa2535d8 100644 --- a/src/bugs.rs +++ b/src/bugs.rs @@ -20,6 +20,7 @@ pub static NONE: Bugs = Bugs { respect_min_max_size: false, }; +#[derive(Debug)] pub struct Bugs { pub respect_min_max_size: bool, } diff --git a/src/client/mod.rs b/src/client/mod.rs index 82ad4b7e..555323cd 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -4,12 +4,12 @@ use crate::client::objects::Objects; use crate::ifs::wl_callback::WlCallback; use crate::ifs::wl_display::WlDisplay; use crate::ifs::wl_registry::WlRegistry; +use crate::leaks::Tracker; use crate::object::{Interface, Object, ObjectId, WL_DISPLAY_ID}; use crate::state::State; use crate::utils::asyncevent::AsyncEvent; use crate::utils::buffd::{MsgFormatter, MsgParser, MsgParserError, OutBufferSwapchain}; use crate::utils::numcell::NumCell; -use crate::utils::oneshot::{oneshot, OneshotTx}; use crate::utils::queue::AsyncQueue; use crate::wire::WlRegistryId; use crate::ErrorFmt; @@ -19,6 +19,7 @@ use std::cell::{Cell, RefCell, RefMut}; use std::error::Error; use std::fmt::{Debug, Display, Formatter}; use std::mem; +use std::ops::DerefMut; use std::rc::Rc; use uapi::{c, OwnedFd}; @@ -50,6 +51,11 @@ impl Clients { } } + pub fn clear(&self) { + mem::take(self.clients.borrow_mut().deref_mut()); + mem::take(self.shutdown_clients.borrow_mut().deref_mut()); + } + pub fn id(&self) -> ClientId { ClientId(self.next_client_id.fetch_add(1)) } @@ -86,7 +92,6 @@ impl Clients { } } }; - let (send, recv) = oneshot(); let data = Rc::new(Client { id, state: global.clone(), @@ -95,14 +100,17 @@ impl Clients { objects: Objects::new(), swapchain: Default::default(), flush_request: Default::default(), - shutdown: Cell::new(Some(send)), + shutdown: Default::default(), dispatch_frame_requests: AsyncQueue::new(), + tracker: Default::default(), }); + track!(data, data); let display = Rc::new(WlDisplay::new(&data)); + track!(data, display); data.objects.display.set(Some(display.clone())); data.objects.add_client_object(display).expect(""); let client = ClientHolder { - _handler: global.eng.spawn(tasks::client(data.clone(), recv)), + _handler: global.eng.spawn(tasks::client(data.clone())), data, }; log::info!( @@ -126,7 +134,7 @@ impl Clients { pub fn shutdown(&self, client_id: ClientId) { if let Some(client) = self.clients.borrow_mut().remove(&client_id) { log::info!("Shutting down client {}", client.data.id.0); - client.data.shutdown.replace(None).unwrap().send(()); + client.data.shutdown.trigger(); client.data.flush_request.trigger(); self.shutdown_clients.borrow_mut().insert(client_id, client); } @@ -159,6 +167,8 @@ impl Drop for ClientHolder { fn drop(&mut self) { self.data.objects.destroy(); self.data.dispatch_frame_requests.clear(); + self.data.flush_request.clear(); + self.data.shutdown.clear(); } } @@ -180,8 +190,9 @@ pub struct Client { pub objects: Objects, swapchain: Rc>, flush_request: AsyncEvent, - shutdown: Cell>>, + shutdown: AsyncEvent, pub dispatch_frame_requests: AsyncQueue>, + pub tracker: Tracker, } const MAX_PENDING_BUFFERS: usize = 10; diff --git a/src/client/objects.rs b/src/client/objects.rs index 566ef167..99c830ee 100644 --- a/src/client/objects.rs +++ b/src/client/objects.rs @@ -22,6 +22,7 @@ use crate::wire::{ use ahash::AHashMap; use std::cell::{RefCell, RefMut}; use std::mem; +use std::ops::DerefMut; use std::rc::Rc; pub struct Objects { @@ -71,14 +72,14 @@ impl Objects { for obj in toplevel.values_mut() { obj.destroy_node(true); } - toplevel.clear(); + mem::take(toplevel.deref_mut()); } { let mut registry = self.registry.lock(); for obj in registry.values_mut() { obj.break_loops(); } - registry.clear(); + mem::take(registry.deref_mut()); } self.display.set(None); self.registries.clear(); diff --git a/src/client/tasks.rs b/src/client/tasks.rs index 0188104b..ff157afd 100644 --- a/src/client/tasks.rs +++ b/src/client/tasks.rs @@ -1,7 +1,6 @@ use crate::client::{Client, ClientError}; use crate::object::ObjectId; use crate::utils::buffd::{BufFdIn, BufFdOut, MsgParser}; -use crate::utils::oneshot::OneshotRx; use crate::utils::vec_ext::VecExt; use crate::ErrorFmt; use futures::{select, FutureExt}; @@ -9,14 +8,15 @@ use std::collections::VecDeque; use std::mem; use std::rc::Rc; -pub async fn client(data: Rc, shutdown: OneshotRx<()>) { +pub async fn client(data: Rc) { let mut recv = data.state.eng.spawn(receive(data.clone())).fuse(); let mut dispatch_fr = data.state.eng.spawn(dispatch_fr(data.clone())).fuse(); + let mut shutdown = data.shutdown.triggered().fuse(); let _send = data.state.eng.spawn(send(data.clone())); select! { _ = recv => { }, _ = dispatch_fr => { }, - _ = shutdown.fuse() => { }, + _ = shutdown => { }, } drop(recv); drop(dispatch_fr); diff --git a/src/ifs/ipc/mod.rs b/src/ifs/ipc/mod.rs index 64150243..eaed02d2 100644 --- a/src/ifs/ipc/mod.rs +++ b/src/ifs/ipc/mod.rs @@ -47,7 +47,7 @@ pub trait Vtable: Sized { dd: &Rc, data: OfferData, id: ObjectId, - ) -> Self::Offer; + ) -> Rc; fn send_selection(dd: &Self::Device, offer: Self::OfferId); fn send_cancelled(source: &Self::Source); fn get_offer_id(offer: &Self::Offer) -> Self::OfferId; @@ -215,7 +215,7 @@ pub fn offer_source_to(src: &Rc, client: &Rc) { client: client.clone(), shared: shared.clone(), }; - let offer = Rc::new(T::create_offer(client, dd, offer_data, id)); + let offer = T::create_offer(client, dd, offer_data, id); data.offers.insert(id.into(), offer.clone()); let mt = data.mime_types.borrow_mut(); T::send_offer(dd, &offer); diff --git a/src/ifs/ipc/wl_data_device.rs b/src/ifs/ipc/wl_data_device.rs index 62c5e056..e09d1ab9 100644 --- a/src/ifs/ipc/wl_data_device.rs +++ b/src/ifs/ipc/wl_data_device.rs @@ -7,6 +7,8 @@ use crate::ifs::ipc::{ break_device_loops, destroy_device, DeviceData, OfferData, Role, SourceData, Vtable, }; use crate::ifs::wl_seat::{WlSeat, WlSeatError, WlSeatGlobal}; +use crate::ifs::wl_surface::{SurfaceRole, WlSurfaceError}; +use crate::leaks::Tracker; use crate::object::{Object, ObjectId}; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; @@ -15,7 +17,6 @@ use crate::wire::{WlDataDeviceId, WlDataOfferId, WlSurfaceId}; use std::rc::Rc; use thiserror::Error; use uapi::OwnedFd; -use crate::ifs::wl_surface::{SurfaceRole, WlSurfaceError}; #[allow(dead_code)] const ROLE: u32 = 0; @@ -25,6 +26,7 @@ pub struct WlDataDevice { pub manager: Rc, pub seat: Rc, pub data: DeviceData, + pub tracker: Tracker, } impl WlDataDevice { @@ -34,6 +36,7 @@ impl WlDataDevice { manager: manager.clone(), seat: seat.clone(), data: Default::default(), + tracker: Default::default(), } } @@ -153,13 +156,16 @@ impl Vtable for WlDataDevice { device: &Rc, offer_data: OfferData, id: ObjectId, - ) -> Self::Offer { - WlDataOffer { + ) -> Rc { + let rc = Rc::new(WlDataOffer { id: id.into(), client: client.clone(), device: device.clone(), data: offer_data, - } + tracker: Default::default(), + }); + track!(client, rc); + rc } fn send_selection(dd: &Self::Device, offer: Self::OfferId) { diff --git a/src/ifs/ipc/wl_data_device_manager.rs b/src/ifs/ipc/wl_data_device_manager.rs index 8d5a1664..14a205da 100644 --- a/src/ifs/ipc/wl_data_device_manager.rs +++ b/src/ifs/ipc/wl_data_device_manager.rs @@ -2,6 +2,7 @@ use crate::client::{Client, ClientError}; use crate::globals::{Global, GlobalName}; use crate::ifs::ipc::wl_data_device::WlDataDevice; use crate::ifs::ipc::wl_data_source::WlDataSource; +use crate::leaks::Tracker; use crate::object::Object; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; @@ -27,6 +28,7 @@ pub struct WlDataDeviceManager { pub id: WlDataDeviceManagerId, pub client: Rc, pub version: u32, + tracker: Tracker, } impl WlDataDeviceManagerGlobal { @@ -44,7 +46,9 @@ impl WlDataDeviceManagerGlobal { id, client: client.clone(), version, + tracker: Default::default(), }); + track!(client, obj); client.add_client_obj(&obj)?; Ok(()) } @@ -54,6 +58,7 @@ impl WlDataDeviceManager { fn create_data_source(&self, parser: MsgParser<'_, '_>) -> Result<(), CreateDataSourceError> { let req: CreateDataSource = self.client.parse(self, parser)?; let res = Rc::new(WlDataSource::new(req.id, &self.client)); + track!(self.client, res); self.client.add_client_obj(&res)?; Ok(()) } @@ -65,6 +70,7 @@ impl WlDataDeviceManager { let req: GetDataDevice = self.client.parse(&**self, parser)?; let seat = self.client.lookup(req.seat)?; let dev = Rc::new(WlDataDevice::new(req.id, self, &seat)); + track!(self.client, dev); seat.add_data_device(&dev); self.client.add_client_obj(&dev)?; Ok(()) diff --git a/src/ifs/ipc/wl_data_offer.rs b/src/ifs/ipc/wl_data_offer.rs index 8dacc9a3..0a8f44dc 100644 --- a/src/ifs/ipc/wl_data_offer.rs +++ b/src/ifs/ipc/wl_data_offer.rs @@ -5,6 +5,7 @@ use crate::ifs::ipc::{ break_offer_loops, destroy_offer, receive, OfferData, Role, OFFER_STATE_ACCEPTED, OFFER_STATE_DROPPED, OFFER_STATE_FINISHED, SOURCE_STATE_FINISHED, }; +use crate::leaks::Tracker; use crate::object::Object; use crate::utils::bitflags::BitflagsExt; use crate::utils::buffd::MsgParser; @@ -28,6 +29,7 @@ pub struct WlDataOffer { pub client: Rc, pub device: Rc, pub data: OfferData, + pub tracker: Tracker, } impl WlDataOffer { diff --git a/src/ifs/ipc/wl_data_source.rs b/src/ifs/ipc/wl_data_source.rs index e98998d9..445f0a1d 100644 --- a/src/ifs/ipc/wl_data_source.rs +++ b/src/ifs/ipc/wl_data_source.rs @@ -6,6 +6,7 @@ use crate::ifs::ipc::{ add_mime_type, break_source_loops, cancel_offers, destroy_source, SharedState, SourceData, OFFER_STATE_ACCEPTED, OFFER_STATE_DROPPED, }; +use crate::leaks::Tracker; use crate::object::Object; use crate::utils::bitflags::BitflagsExt; use crate::utils::buffd::MsgParser; @@ -24,12 +25,14 @@ const INVALID_SOURCE: u32 = 1; pub struct WlDataSource { pub id: WlDataSourceId, pub data: SourceData, + pub tracker: Tracker, } impl WlDataSource { pub fn new(id: WlDataSourceId, client: &Rc) -> Self { Self { id, + tracker: Default::default(), data: SourceData::new(client), } } diff --git a/src/ifs/ipc/zwp_primary_selection_device_manager_v1.rs b/src/ifs/ipc/zwp_primary_selection_device_manager_v1.rs index 073fb623..c8a828be 100644 --- a/src/ifs/ipc/zwp_primary_selection_device_manager_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_device_manager_v1.rs @@ -2,6 +2,7 @@ use crate::client::{Client, ClientError}; use crate::globals::{Global, GlobalName}; use crate::ifs::ipc::zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1; use crate::ifs::ipc::zwp_primary_selection_source_v1::ZwpPrimarySelectionSourceV1; +use crate::leaks::Tracker; use crate::object::Object; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; @@ -18,6 +19,7 @@ pub struct ZwpPrimarySelectionDeviceManagerV1 { pub id: ZwpPrimarySelectionDeviceManagerV1Id, pub client: Rc, pub version: u32, + pub tracker: Tracker, } impl ZwpPrimarySelectionDeviceManagerV1Global { @@ -35,7 +37,9 @@ impl ZwpPrimarySelectionDeviceManagerV1Global { id, client: client.clone(), version, + tracker: Default::default(), }); + track!(client, obj); client.add_client_obj(&obj)?; Ok(()) } @@ -45,6 +49,7 @@ impl ZwpPrimarySelectionDeviceManagerV1 { fn create_source(&self, parser: MsgParser<'_, '_>) -> Result<(), CreateSourceError> { let req: CreateSource = self.client.parse(self, parser)?; let res = Rc::new(ZwpPrimarySelectionSourceV1::new(req.id, &self.client)); + track!(self.client, res); self.client.add_client_obj(&res)?; Ok(()) } @@ -53,6 +58,7 @@ impl ZwpPrimarySelectionDeviceManagerV1 { let req: GetDevice = self.client.parse(&**self, parser)?; let seat = self.client.lookup(req.seat)?; let dev = Rc::new(ZwpPrimarySelectionDeviceV1::new(req.id, self, &seat)); + track!(self.client, dev); seat.add_primary_selection_device(&dev); self.client.add_client_obj(&dev)?; Ok(()) diff --git a/src/ifs/ipc/zwp_primary_selection_device_v1.rs b/src/ifs/ipc/zwp_primary_selection_device_v1.rs index f3ad4061..cb89f6ab 100644 --- a/src/ifs/ipc/zwp_primary_selection_device_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_device_v1.rs @@ -6,6 +6,7 @@ use crate::ifs::ipc::{ break_device_loops, destroy_device, DeviceData, OfferData, Role, SourceData, Vtable, }; use crate::ifs::wl_seat::{WlSeat, WlSeatError, WlSeatGlobal}; +use crate::leaks::Tracker; use crate::object::{Object, ObjectId}; use crate::utils::buffd::{MsgParser, MsgParserError}; use crate::wire::zwp_primary_selection_device_v1::*; @@ -19,6 +20,7 @@ pub struct ZwpPrimarySelectionDeviceV1 { pub manager: Rc, seat: Rc, data: DeviceData, + pub tracker: Tracker, } impl ZwpPrimarySelectionDeviceV1 { @@ -32,6 +34,7 @@ impl ZwpPrimarySelectionDeviceV1 { manager: manager.clone(), seat: seat.clone(), data: DeviceData::default(), + tracker: Default::default(), } } @@ -104,12 +107,15 @@ impl Vtable for ZwpPrimarySelectionDeviceV1 { _device: &Rc, offer_data: OfferData, id: ObjectId, - ) -> Self::Offer { - ZwpPrimarySelectionOfferV1 { + ) -> Rc { + let rc = Rc::new(ZwpPrimarySelectionOfferV1 { id: id.into(), client: client.clone(), offer_data, - } + tracker: Default::default(), + }); + track!(client, rc); + rc } fn send_selection(dd: &Self::Device, offer: Self::OfferId) { diff --git a/src/ifs/ipc/zwp_primary_selection_offer_v1.rs b/src/ifs/ipc/zwp_primary_selection_offer_v1.rs index 27aac843..e5021fa7 100644 --- a/src/ifs/ipc/zwp_primary_selection_offer_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_offer_v1.rs @@ -1,6 +1,7 @@ use crate::client::{Client, ClientError}; use crate::ifs::ipc::zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1; use crate::ifs::ipc::{break_offer_loops, destroy_offer, receive, OfferData}; +use crate::leaks::Tracker; use crate::object::Object; use crate::utils::buffd::{MsgParser, MsgParserError}; use crate::wire::zwp_primary_selection_offer_v1::*; @@ -12,6 +13,7 @@ pub struct ZwpPrimarySelectionOfferV1 { pub id: ZwpPrimarySelectionOfferV1Id, pub client: Rc, pub offer_data: OfferData, + pub tracker: Tracker, } impl ZwpPrimarySelectionOfferV1 { diff --git a/src/ifs/ipc/zwp_primary_selection_source_v1.rs b/src/ifs/ipc/zwp_primary_selection_source_v1.rs index def96cd5..bbba0d18 100644 --- a/src/ifs/ipc/zwp_primary_selection_source_v1.rs +++ b/src/ifs/ipc/zwp_primary_selection_source_v1.rs @@ -1,6 +1,7 @@ use crate::client::{Client, ClientError}; use crate::ifs::ipc::zwp_primary_selection_device_v1::ZwpPrimarySelectionDeviceV1; use crate::ifs::ipc::{add_mime_type, break_source_loops, destroy_source, SourceData}; +use crate::leaks::Tracker; use crate::object::Object; use crate::utils::buffd::{MsgParser, MsgParserError}; use crate::wire::zwp_primary_selection_source_v1::*; @@ -12,6 +13,7 @@ use uapi::OwnedFd; pub struct ZwpPrimarySelectionSourceV1 { pub id: ZwpPrimarySelectionSourceV1Id, pub data: SourceData, + pub tracker: Tracker, } impl ZwpPrimarySelectionSourceV1 { @@ -19,6 +21,7 @@ impl ZwpPrimarySelectionSourceV1 { Self { id, data: SourceData::new(client), + tracker: Default::default(), } } diff --git a/src/ifs/org_kde_kwin_server_decoration.rs b/src/ifs/org_kde_kwin_server_decoration.rs index 8396c35a..d2c6041c 100644 --- a/src/ifs/org_kde_kwin_server_decoration.rs +++ b/src/ifs/org_kde_kwin_server_decoration.rs @@ -1,4 +1,5 @@ use crate::client::{Client, ClientError}; +use crate::leaks::Tracker; use crate::object::Object; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; @@ -18,6 +19,7 @@ pub struct OrgKdeKwinServerDecoration { id: OrgKdeKwinServerDecorationId, client: Rc, requested: Cell, + pub tracker: Tracker, } impl OrgKdeKwinServerDecoration { @@ -26,6 +28,7 @@ impl OrgKdeKwinServerDecoration { id, client: client.clone(), requested: Cell::new(false), + tracker: Default::default(), } } diff --git a/src/ifs/org_kde_kwin_server_decoration_manager.rs b/src/ifs/org_kde_kwin_server_decoration_manager.rs index 462d41d4..05b80a13 100644 --- a/src/ifs/org_kde_kwin_server_decoration_manager.rs +++ b/src/ifs/org_kde_kwin_server_decoration_manager.rs @@ -1,6 +1,7 @@ use crate::client::{Client, ClientError}; use crate::globals::{Global, GlobalName}; use crate::ifs::org_kde_kwin_server_decoration::OrgKdeKwinServerDecoration; +use crate::leaks::Tracker; use crate::object::Object; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; @@ -33,7 +34,9 @@ impl OrgKdeKwinServerDecorationManagerGlobal { id, client: client.clone(), _version: version, + tracker: Default::default(), }); + track!(client, obj); client.add_client_obj(&obj)?; obj.send_default_mode(SERVER); Ok(()) @@ -62,6 +65,7 @@ pub struct OrgKdeKwinServerDecorationManager { id: OrgKdeKwinServerDecorationManagerId, client: Rc, _version: u32, + pub tracker: Tracker, } impl OrgKdeKwinServerDecorationManager { @@ -76,6 +80,7 @@ impl OrgKdeKwinServerDecorationManager { let req: Create = self.client.parse(self, parser)?; let _ = self.client.lookup(req.surface)?; let obj = Rc::new(OrgKdeKwinServerDecoration::new(req.id, &self.client)); + track!(self.client, obj); self.client.add_client_obj(&obj)?; obj.send_mode(SERVER); Ok(()) diff --git a/src/ifs/wl_buffer.rs b/src/ifs/wl_buffer.rs index c016aca7..072a3f3e 100644 --- a/src/ifs/wl_buffer.rs +++ b/src/ifs/wl_buffer.rs @@ -1,6 +1,7 @@ use crate::client::{Client, ClientError}; use crate::clientmem::{ClientMem, ClientMemOffset}; use crate::format::Format; +use crate::leaks::Tracker; use crate::object::Object; use crate::rect::Rect; use crate::render::{Image, Texture}; @@ -29,6 +30,7 @@ pub struct WlBuffer { pub texture: CloneCell>>, width: i32, height: i32, + pub tracker: Tracker, } impl WlBuffer { @@ -55,6 +57,7 @@ impl WlBuffer { height, texture: CloneCell::new(None), storage: WlBufferStorage::Dmabuf(img.clone()), + tracker: Default::default(), } } @@ -89,6 +92,7 @@ impl WlBuffer { width, height, texture: CloneCell::new(None), + tracker: Default::default(), }) } diff --git a/src/ifs/wl_callback.rs b/src/ifs/wl_callback.rs index 2585a74a..067b2090 100644 --- a/src/ifs/wl_callback.rs +++ b/src/ifs/wl_callback.rs @@ -1,4 +1,5 @@ use crate::client::Client; +use crate::leaks::Tracker; use crate::object::Object; use crate::wire::wl_callback::*; use crate::wire::WlCallbackId; @@ -8,6 +9,7 @@ use thiserror::Error; pub struct WlCallback { client: Rc, id: WlCallbackId, + pub tracker: Tracker, } impl WlCallback { @@ -15,6 +17,7 @@ impl WlCallback { Self { client: client.clone(), id, + tracker: Default::default(), } } diff --git a/src/ifs/wl_compositor.rs b/src/ifs/wl_compositor.rs index 14673e8c..0875054e 100644 --- a/src/ifs/wl_compositor.rs +++ b/src/ifs/wl_compositor.rs @@ -2,6 +2,7 @@ use crate::client::{Client, ClientError}; use crate::globals::{Global, GlobalName}; use crate::ifs::wl_region::WlRegion; use crate::ifs::wl_surface::WlSurface; +use crate::leaks::Tracker; use crate::object::Object; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; @@ -18,6 +19,7 @@ pub struct WlCompositor { id: WlCompositorId, client: Rc, _version: u32, + pub tracker: Tracker, } impl WlCompositorGlobal { @@ -35,7 +37,9 @@ impl WlCompositorGlobal { id, client: client.clone(), _version: version, + tracker: Default::default(), }); + track!(client, obj); client.add_client_obj(&obj)?; Ok(()) } @@ -45,6 +49,7 @@ impl WlCompositor { fn create_surface(&self, parser: MsgParser<'_, '_>) -> Result<(), CreateSurfaceError> { let surface: CreateSurface = self.client.parse(self, parser)?; let surface = Rc::new(WlSurface::new(surface.id, &self.client)); + track!(self.client, surface); self.client.add_client_obj(&surface)?; Ok(()) } @@ -52,6 +57,7 @@ impl WlCompositor { fn create_region(&self, parser: MsgParser<'_, '_>) -> Result<(), CreateRegionError> { let region: CreateRegion = self.client.parse(self, parser)?; let region = Rc::new(WlRegion::new(region.id, &self.client)); + track!(self.client, region); self.client.add_client_obj(®ion)?; Ok(()) } diff --git a/src/ifs/wl_display.rs b/src/ifs/wl_display.rs index b3b087da..83ea6635 100644 --- a/src/ifs/wl_display.rs +++ b/src/ifs/wl_display.rs @@ -2,6 +2,7 @@ use crate::client::{Client, ClientError}; use crate::globals::GlobalsError; use crate::ifs::wl_callback::WlCallback; use crate::ifs::wl_registry::WlRegistry; +use crate::leaks::Tracker; use crate::object::{Object, ObjectId, WL_DISPLAY_ID}; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; @@ -17,8 +18,9 @@ const NO_MEMORY: u32 = 2; const IMPLEMENTATION: u32 = 3; pub struct WlDisplay { - id: WlDisplayId, - client: Rc, + pub id: WlDisplayId, + pub client: Rc, + pub tracker: Tracker, } impl WlDisplay { @@ -26,12 +28,14 @@ impl WlDisplay { Self { id: WL_DISPLAY_ID, client: client.clone(), + tracker: Default::default(), } } fn sync(&self, parser: MsgParser<'_, '_>) -> Result<(), SyncError> { let sync: Sync = self.client.parse(self, parser)?; let cb = Rc::new(WlCallback::new(sync.callback, &self.client)); + track!(self.client, cb); self.client.add_client_obj(&cb)?; cb.send_done(); self.client.remove_obj(&*cb)?; @@ -41,6 +45,7 @@ impl WlDisplay { fn get_registry(&self, parser: MsgParser<'_, '_>) -> Result<(), GetRegistryError> { let gr: GetRegistry = self.client.parse(self, parser)?; let registry = Rc::new(WlRegistry::new(gr.registry, &self.client)); + track!(self.client, registry); self.client.add_client_obj(®istry)?; self.client.state.globals.notify_all(®istry); Ok(()) diff --git a/src/ifs/wl_drm.rs b/src/ifs/wl_drm.rs index 66d3ff4b..b62a8374 100644 --- a/src/ifs/wl_drm.rs +++ b/src/ifs/wl_drm.rs @@ -1,5 +1,6 @@ use crate::client::{Client, ClientError}; use crate::globals::{Global, GlobalName}; +use crate::leaks::Tracker; use crate::object::Object; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; @@ -31,7 +32,9 @@ impl WlDrmGlobal { id, client: client.clone(), _version: version, + tracker: Default::default(), }); + track!(client, obj); client.add_client_obj(&obj)?; if let Some(rc) = client.state.render_ctx.get() { obj.send_device(&rc.render_node()); @@ -59,6 +62,7 @@ pub struct WlDrm { id: WlDrmId, pub client: Rc, _version: u32, + tracker: Tracker, } impl WlDrm { diff --git a/src/ifs/wl_output.rs b/src/ifs/wl_output.rs index de16f00d..e1c1c375 100644 --- a/src/ifs/wl_output.rs +++ b/src/ifs/wl_output.rs @@ -1,6 +1,7 @@ use crate::backend::Output; use crate::client::{Client, ClientError, ClientId}; use crate::globals::{Global, GlobalName}; +use crate::leaks::Tracker; use crate::object::Object; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; @@ -100,7 +101,9 @@ impl WlOutputGlobal { id, client: client.clone(), version, + tracker: Default::default(), }); + track!(client, obj); client.add_client_obj(&obj)?; self.bindings .borrow_mut() @@ -142,6 +145,7 @@ pub struct WlOutput { pub id: WlOutputId, client: Rc, version: u32, + tracker: Tracker, } pub const SEND_DONE_SINCE: u32 = 2; diff --git a/src/ifs/wl_region.rs b/src/ifs/wl_region.rs index 77140029..4d18619b 100644 --- a/src/ifs/wl_region.rs +++ b/src/ifs/wl_region.rs @@ -1,4 +1,5 @@ use crate::client::{Client, ClientError}; +use crate::leaks::Tracker; use crate::object::Object; use crate::pixman::Region; use crate::utils::buffd::MsgParser; @@ -13,6 +14,7 @@ pub struct WlRegion { id: WlRegionId, client: Rc, rect: RefCell, + pub tracker: Tracker, } impl WlRegion { @@ -21,6 +23,7 @@ impl WlRegion { id, client: client.clone(), rect: RefCell::new(Region::new()), + tracker: Default::default(), } } diff --git a/src/ifs/wl_registry.rs b/src/ifs/wl_registry.rs index efe2d442..551b270f 100644 --- a/src/ifs/wl_registry.rs +++ b/src/ifs/wl_registry.rs @@ -1,5 +1,6 @@ use crate::client::Client; use crate::globals::{Global, GlobalName, GlobalsError}; +use crate::leaks::Tracker; use crate::object::{Interface, Object}; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; @@ -11,6 +12,7 @@ use thiserror::Error; pub struct WlRegistry { id: WlRegistryId, client: Rc, + pub tracker: Tracker, } impl WlRegistry { @@ -18,6 +20,7 @@ impl WlRegistry { Self { id, client: client.clone(), + tracker: Default::default(), } } diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index 567dfb00..0715ba56 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -21,6 +21,7 @@ use crate::ifs::wl_seat::wl_pointer::WlPointer; use crate::ifs::wl_seat::wl_touch::WlTouch; use crate::ifs::wl_surface::xdg_surface::xdg_toplevel::XdgToplevel; use crate::ifs::wl_surface::WlSurface; +use crate::leaks::Tracker; use crate::object::{Object, ObjectId}; use crate::tree::{FloatNode, FoundNode, Node}; use crate::utils::asyncevent::AsyncEvent; @@ -41,6 +42,8 @@ pub use event_handling::NodeSeatState; use std::cell::{Cell, RefCell}; use std::collections::hash_map::Entry; use std::io::Write; +use std::mem; +use std::ops::DerefMut; use std::rc::Rc; use thiserror::Error; use uapi::{c, OwnedFd}; @@ -266,6 +269,11 @@ impl WlSeatGlobal { self.cursor.get() } + pub fn clear(&self) { + mem::take(self.pointer_stack.borrow_mut().deref_mut()); + mem::take(self.found_tree.borrow_mut().deref_mut()); + } + pub fn id(&self) -> SeatId { self.seat.id() } @@ -283,7 +291,9 @@ impl WlSeatGlobal { pointers: Default::default(), keyboards: Default::default(), version, + tracker: Default::default(), }); + track!(client, obj); client.add_client_obj(&obj)?; obj.send_capabilities(); if version >= SEAT_NAME_SINCE { @@ -323,6 +333,7 @@ pub struct WlSeat { pointers: CopyHashMap>, keyboards: CopyHashMap>, version: u32, + tracker: Tracker, } impl WlSeat { @@ -381,6 +392,7 @@ impl WlSeat { fn get_pointer(self: &Rc, parser: MsgParser<'_, '_>) -> Result<(), GetPointerError> { let req: GetPointer = self.client.parse(&**self, parser)?; let p = Rc::new(WlPointer::new(req.id, self)); + track!(self.client, p); self.client.add_client_obj(&p)?; self.pointers.set(req.id, p); Ok(()) @@ -389,6 +401,7 @@ impl WlSeat { fn get_keyboard(self: &Rc, parser: MsgParser<'_, '_>) -> Result<(), GetKeyboardError> { let req: GetKeyboard = self.client.parse(&**self, parser)?; let p = Rc::new(WlKeyboard::new(req.id, self)); + track!(self.client, p); self.client.add_client_obj(&p)?; self.keyboards.set(req.id, p.clone()); p.send_keymap(wl_keyboard::XKB_V1, p.keymap_fd()?, self.global.layout_size); @@ -401,6 +414,7 @@ impl WlSeat { fn get_touch(self: &Rc, parser: MsgParser<'_, '_>) -> Result<(), GetTouchError> { let req: GetTouch = self.client.parse(&**self, parser)?; let p = Rc::new(WlTouch::new(req.id, self)); + track!(self.client, p); self.client.add_client_obj(&p)?; Ok(()) } @@ -409,8 +423,11 @@ impl WlSeat { let _req: Release = self.client.parse(self, parser)?; { let mut bindings = self.global.bindings.borrow_mut(); - if let Some(hm) = bindings.get_mut(&self.client.id) { - hm.remove(&self.id); + if let Entry::Occupied(mut hm) = bindings.entry(self.client.id) { + hm.get_mut().remove(&self.id); + if hm.get().is_empty() { + hm.remove(); + } } } self.client.remove_obj(self)?; @@ -439,8 +456,11 @@ impl Object for WlSeat { fn break_loops(&self) { { let mut bindings = self.global.bindings.borrow_mut(); - if let Some(hm) = bindings.get_mut(&self.client.id) { - hm.remove(&self.id); + if let Entry::Occupied(mut hm) = bindings.entry(self.client.id) { + hm.get_mut().remove(&self.id); + if hm.get().is_empty() { + hm.remove(); + } } } self.pointers.clear(); diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index f09dd3f8..870f0330 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -85,6 +85,8 @@ impl NodeSeatState { if last.id() == node_id { break; } + last.seat_state().leave(&seat); + last.leave(&seat); } seat.state.tree_changed(); } diff --git a/src/ifs/wl_seat/pointer_owner.rs b/src/ifs/wl_seat/pointer_owner.rs index 90f659c4..2e09d080 100644 --- a/src/ifs/wl_seat/pointer_owner.rs +++ b/src/ifs/wl_seat/pointer_owner.rs @@ -4,7 +4,7 @@ use crate::ifs::ipc; use crate::ifs::ipc::wl_data_device::WlDataDevice; use crate::ifs::ipc::wl_data_source::WlDataSource; use crate::ifs::wl_seat::{Dnd, DroppedDnd, WlSeatError, WlSeatGlobal}; -use crate::ifs::wl_surface::{WlSurface}; +use crate::ifs::wl_surface::WlSurface; use crate::tree::{FoundNode, Node}; use crate::utils::clonecell::CloneCell; use crate::utils::smallmap::SmallMap; diff --git a/src/ifs/wl_seat/wl_keyboard.rs b/src/ifs/wl_seat/wl_keyboard.rs index c4c256a8..f878fc45 100644 --- a/src/ifs/wl_seat/wl_keyboard.rs +++ b/src/ifs/wl_seat/wl_keyboard.rs @@ -1,5 +1,6 @@ use crate::client::ClientError; use crate::ifs::wl_seat::WlSeat; +use crate::leaks::Tracker; use crate::object::Object; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; @@ -21,6 +22,7 @@ pub(super) const PRESSED: u32 = 1; pub struct WlKeyboard { id: WlKeyboardId, seat: Rc, + pub tracker: Tracker, } impl WlKeyboard { @@ -28,6 +30,7 @@ impl WlKeyboard { Self { id, seat: seat.clone(), + tracker: Default::default(), } } diff --git a/src/ifs/wl_seat/wl_pointer.rs b/src/ifs/wl_seat/wl_pointer.rs index 52e6edc7..ee5ef2a6 100644 --- a/src/ifs/wl_seat/wl_pointer.rs +++ b/src/ifs/wl_seat/wl_pointer.rs @@ -3,6 +3,7 @@ use crate::cursor::Cursor; use crate::fixed::Fixed; use crate::ifs::wl_seat::WlSeat; use crate::ifs::wl_surface::WlSurfaceError; +use crate::leaks::Tracker; use crate::object::Object; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; @@ -34,6 +35,7 @@ pub const POINTER_FRAME_SINCE_VERSION: u32 = 5; pub struct WlPointer { id: WlPointerId, seat: Rc, + pub tracker: Tracker, } impl WlPointer { @@ -41,6 +43,7 @@ impl WlPointer { Self { id, seat: seat.clone(), + tracker: Default::default(), } } diff --git a/src/ifs/wl_seat/wl_touch.rs b/src/ifs/wl_seat/wl_touch.rs index 39ef0a3d..ffeb04fb 100644 --- a/src/ifs/wl_seat/wl_touch.rs +++ b/src/ifs/wl_seat/wl_touch.rs @@ -1,5 +1,6 @@ use crate::client::ClientError; use crate::ifs::wl_seat::WlSeat; +use crate::leaks::Tracker; use crate::object::Object; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; @@ -26,6 +27,7 @@ const ORIENTATION: u32 = 6; pub struct WlTouch { id: WlTouchId, seat: Rc, + pub tracker: Tracker, } impl WlTouch { @@ -33,6 +35,7 @@ impl WlTouch { Self { id, seat: seat.clone(), + tracker: Default::default(), } } diff --git a/src/ifs/wl_shm.rs b/src/ifs/wl_shm.rs index dd523920..1bdd24e8 100644 --- a/src/ifs/wl_shm.rs +++ b/src/ifs/wl_shm.rs @@ -2,6 +2,7 @@ use crate::client::{Client, ClientError}; use crate::format::FORMATS; use crate::globals::{Global, GlobalName}; use crate::ifs::wl_shm_pool::{WlShmPool, WlShmPoolError}; +use crate::leaks::Tracker; use crate::object::Object; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; @@ -18,6 +19,7 @@ pub struct WlShm { _global: Rc, id: WlShmId, client: Rc, + pub tracker: Tracker, } impl WlShmGlobal { @@ -35,7 +37,9 @@ impl WlShmGlobal { _global: self, id, client: client.clone(), + tracker: Default::default(), }); + track!(client, obj); client.add_client_obj(&obj)?; for format in FORMATS { client.event(Format { @@ -59,6 +63,7 @@ impl WlShm { create.fd, create.size as usize, )?); + track!(self.client, pool); self.client.add_client_obj(&pool)?; Ok(()) } diff --git a/src/ifs/wl_shm_pool.rs b/src/ifs/wl_shm_pool.rs index c6530028..af50250c 100644 --- a/src/ifs/wl_shm_pool.rs +++ b/src/ifs/wl_shm_pool.rs @@ -2,6 +2,7 @@ use crate::client::{Client, ClientError}; use crate::clientmem::ClientMem; use crate::format::{formats, map_wayland_format_id}; use crate::ifs::wl_buffer::{WlBuffer, WlBufferError}; +use crate::leaks::Tracker; use crate::object::Object; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; @@ -18,6 +19,7 @@ pub struct WlShmPool { client: Rc, fd: Rc, mem: CloneCell>, + pub tracker: Tracker, } impl WlShmPool { @@ -32,6 +34,7 @@ impl WlShmPool { client: client.clone(), mem: CloneCell::new(Rc::new(ClientMem::new(fd.raw(), len)?)), fd, + tracker: Default::default(), }) } @@ -55,6 +58,7 @@ impl WlShmPool { format, &self.mem.get(), )?); + track!(self.client, buffer); self.client.add_client_obj(&buffer)?; Ok(()) } diff --git a/src/ifs/wl_subcompositor.rs b/src/ifs/wl_subcompositor.rs index 857bc988..b5468242 100644 --- a/src/ifs/wl_subcompositor.rs +++ b/src/ifs/wl_subcompositor.rs @@ -1,6 +1,7 @@ use crate::client::{Client, ClientError}; use crate::globals::{Global, GlobalName}; use crate::ifs::wl_surface::wl_subsurface::{WlSubsurface, WlSubsurfaceError}; +use crate::leaks::Tracker; use crate::object::Object; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; @@ -19,6 +20,7 @@ pub struct WlSubcompositorGlobal { pub struct WlSubcompositor { id: WlSubcompositorId, client: Rc, + pub tracker: Tracker, } impl WlSubcompositorGlobal { @@ -35,7 +37,9 @@ impl WlSubcompositorGlobal { let obj = Rc::new(WlSubcompositor { id, client: client.clone(), + tracker: Default::default(), }); + track!(client, obj); client.add_client_obj(&obj)?; Ok(()) } @@ -53,6 +57,7 @@ impl WlSubcompositor { let surface = self.client.lookup(req.surface)?; let parent = self.client.lookup(req.parent)?; let subsurface = Rc::new(WlSubsurface::new(req.id, &surface, &parent)); + track!(self.client, subsurface); self.client.add_client_obj(&subsurface)?; subsurface.install()?; Ok(()) diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index f2efbb5a..bafe9312 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -11,6 +11,7 @@ use crate::ifs::wl_seat::{Dnd, NodeSeatState, WlSeatGlobal}; use crate::ifs::wl_surface::cursor::CursorSurface; use crate::ifs::wl_surface::wl_subsurface::WlSubsurface; use crate::ifs::wl_surface::xdg_surface::{XdgSurface, XdgSurfaceError, XdgSurfaceRole}; +use crate::leaks::Tracker; use crate::object::Object; use crate::pixman::Region; use crate::rect::Rect; @@ -26,6 +27,7 @@ use crate::xkbcommon::ModifierState; use crate::NumCell; use ahash::AHashMap; use std::cell::{Cell, RefCell}; +use std::fmt::{Debug, Formatter}; use std::mem; use std::ops::{Deref, DerefMut}; use std::rc::Rc; @@ -80,6 +82,13 @@ pub struct WlSurface { xdg: CloneCell>>, cursors: SmallMap, 1>, pub dnd_icons: SmallMap, 1>, + pub tracker: Tracker, +} + +impl Debug for WlSurface { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("WlSurface").finish_non_exhaustive() + } } #[derive(Debug, Copy, Clone, Eq, PartialEq)] @@ -176,6 +185,7 @@ impl WlSurface { xdg: Default::default(), cursors: Default::default(), dnd_icons: Default::default(), + tracker: Default::default(), } } @@ -204,6 +214,7 @@ impl WlSurface { } self.set_role(SurfaceRole::Cursor)?; let cursor = Rc::new(CursorSurface::new(seat, self)); + track!(self.client, cursor); cursor.handle_buffer_change(); self.cursors.insert(seat.id(), cursor.clone()); Ok(cursor) @@ -335,6 +346,9 @@ impl WlSurface { } *children = None; } + self.buffer.set(None); + self.frame_requests.borrow_mut().clear(); + self.xdg.set(None); self.client.remove_obj(self)?; Ok(()) } @@ -358,6 +372,7 @@ impl WlSurface { fn frame(&self, parser: MsgParser<'_, '_>) -> Result<(), FrameError> { let req: Frame = self.parse(parser)?; let cb = Rc::new(WlCallback::new(req.callback, &self.client)); + track!(self.client, cb); self.client.add_client_obj(&cb)?; self.pending.frame_request.borrow_mut().push(cb); Ok(()) diff --git a/src/ifs/wl_surface/cursor.rs b/src/ifs/wl_surface/cursor.rs index 75257431..68e0fada 100644 --- a/src/ifs/wl_surface/cursor.rs +++ b/src/ifs/wl_surface/cursor.rs @@ -1,6 +1,7 @@ use crate::cursor::Cursor; use crate::ifs::wl_seat::WlSeatGlobal; use crate::ifs::wl_surface::WlSurface; +use crate::leaks::Tracker; use crate::rect::Rect; use crate::render::Renderer; use std::cell::Cell; @@ -12,6 +13,7 @@ pub struct CursorSurface { hotspot: Cell<(i32, i32)>, pos: Cell<(i32, i32)>, extents: Cell, + pub tracker: Tracker, } impl CursorSurface { @@ -22,6 +24,7 @@ impl CursorSurface { hotspot: Cell::new((0, 0)), pos: Cell::new((0, 0)), extents: Cell::new(Default::default()), + tracker: Default::default(), } } diff --git a/src/ifs/wl_surface/wl_subsurface.rs b/src/ifs/wl_surface/wl_subsurface.rs index f00b276d..a412537e 100644 --- a/src/ifs/wl_surface/wl_subsurface.rs +++ b/src/ifs/wl_surface/wl_subsurface.rs @@ -3,6 +3,7 @@ use crate::ifs::wl_surface::{ CommitAction, CommitContext, StackElement, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError, WlSurfaceId, }; +use crate::leaks::Tracker; use crate::object::Object; use crate::rect::Rect; use crate::utils::buffd::MsgParser; @@ -31,6 +32,7 @@ pub struct WlSubsurface { node: RefCell>>, depth: NumCell, pending: PendingSubsurfaceData, + pub tracker: Tracker, } #[derive(Default)] @@ -85,6 +87,7 @@ impl WlSubsurface { node: RefCell::new(None), depth: NumCell::new(0), pending: Default::default(), + tracker: Default::default(), } } diff --git a/src/ifs/wl_surface/xdg_surface.rs b/src/ifs/wl_surface/xdg_surface.rs index ee5a25a3..06b10afe 100644 --- a/src/ifs/wl_surface/xdg_surface.rs +++ b/src/ifs/wl_surface/xdg_surface.rs @@ -10,6 +10,7 @@ use crate::ifs::wl_surface::{ CommitAction, CommitContext, SurfaceExt, SurfaceRole, WlSurface, WlSurfaceError, }; use crate::ifs::xdg_wm_base::XdgWmBase; +use crate::leaks::Tracker; use crate::object::Object; use crate::rect::Rect; use crate::tree::{FindTreeResult, FoundNode, Node, WorkspaceNode}; @@ -22,6 +23,7 @@ use crate::wire::xdg_surface::*; use crate::wire::{WlSurfaceId, XdgPopupId, XdgSurfaceId}; use crate::NumCell; use std::cell::Cell; +use std::fmt::Debug; use std::rc::Rc; use thiserror::Error; @@ -64,14 +66,15 @@ pub struct XdgSurface { pub(super) focus_surface: SmallMap, 1>, seat_state: NodeSeatState, pub workspace: CloneCell>>, + pub tracker: Tracker, } -#[derive(Default)] +#[derive(Default, Debug)] struct PendingXdgSurfaceData { geometry: Cell>, } -trait XdgSurfaceExt { +pub trait XdgSurfaceExt: Debug { fn initial_configure(self: Rc) -> Result<(), XdgSurfaceError> { Ok(()) } @@ -111,6 +114,7 @@ impl XdgSurface { focus_surface: Default::default(), seat_state: Default::default(), workspace: Default::default(), + tracker: Default::default(), } } @@ -214,6 +218,7 @@ impl XdgSurface { return Err(DestroyError::PopupsNotYetDestroyed); } } + self.focus_surface.clear(); self.surface.set_xdg_surface(None); self.surface.unset_ext(); self.base.surfaces.remove(&self.id); @@ -236,6 +241,7 @@ impl XdgSurface { return Err(GetToplevelError::AlreadyConstructed); } let toplevel = Rc::new(XdgToplevel::new(req.id, self)); + track!(self.surface.client, toplevel); self.surface.client.add_client_obj(&toplevel)?; self.ext.set(Some(toplevel)); Ok(()) @@ -261,6 +267,7 @@ impl XdgSurface { return Err(GetPopupError::AlreadyConstructed); } let popup = Rc::new(XdgPopup::new(req.id, self, parent.as_ref(), &positioner)?); + track!(self.surface.client, popup); self.surface.client.add_client_obj(&popup)?; if let Some(parent) = &parent { parent.popups.set(req.id, popup.clone()); @@ -341,6 +348,9 @@ impl Object for XdgSurface { fn break_loops(&self) { self.focus_surface.take(); + self.ext.take(); + self.popups.clear(); + self.workspace.set(None); } } diff --git a/src/ifs/wl_surface/xdg_surface/xdg_popup.rs b/src/ifs/wl_surface/xdg_surface/xdg_popup.rs index 364036ca..2aa5b9bc 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_popup.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_popup.rs @@ -4,6 +4,7 @@ use crate::fixed::Fixed; use crate::ifs::wl_seat::{NodeSeatState, WlSeatGlobal}; use crate::ifs::wl_surface::xdg_surface::{XdgSurface, XdgSurfaceError, XdgSurfaceExt}; use crate::ifs::xdg_positioner::{XdgPositioned, XdgPositioner, CA}; +use crate::leaks::Tracker; use crate::object::Object; use crate::rect::Rect; use crate::render::Renderer; @@ -15,6 +16,7 @@ use crate::utils::linkedlist::LinkedNode; use crate::wire::xdg_popup::*; use crate::wire::XdgPopupId; use std::cell::{Cell, RefCell}; +use std::fmt::{Debug, Formatter}; use std::rc::Rc; use thiserror::Error; @@ -32,6 +34,13 @@ pub struct XdgPopup { display_link: RefCell>>>, workspace_link: RefCell>>>, pos: RefCell, + pub tracker: Tracker, +} + +impl Debug for XdgPopup { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("XdgPopup").finish_non_exhaustive() + } } impl XdgPopup { @@ -54,6 +63,7 @@ impl XdgPopup { display_link: RefCell::new(None), workspace_link: RefCell::new(None), pos: RefCell::new(pos), + tracker: Default::default(), }) } diff --git a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs index d2efee49..b6a4066e 100644 --- a/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs +++ b/src/ifs/wl_surface/xdg_surface/xdg_toplevel.rs @@ -5,6 +5,7 @@ use crate::cursor::KnownCursor; use crate::fixed::Fixed; use crate::ifs::wl_seat::{NodeSeatState, WlSeatGlobal}; use crate::ifs::wl_surface::xdg_surface::{XdgSurface, XdgSurfaceError, XdgSurfaceExt}; +use crate::leaks::Tracker; use crate::object::Object; use crate::rect::Rect; use crate::render::Renderer; @@ -21,7 +22,9 @@ use crate::{bugs, NumCell}; use ahash::{AHashMap, AHashSet}; use num_derive::FromPrimitive; use std::cell::{Cell, RefCell}; +use std::fmt::{Debug, Formatter}; use std::mem; +use std::ops::Deref; use std::rc::Rc; use thiserror::Error; @@ -78,6 +81,13 @@ pub struct XdgToplevel { min_height: Cell>, max_width: Cell>, max_height: Cell>, + pub tracker: Tracker, +} + +impl Debug for XdgToplevel { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("XdgToplevel").finish_non_exhaustive() + } } impl XdgToplevel { @@ -103,6 +113,7 @@ impl XdgToplevel { min_height: Cell::new(None), max_width: Cell::new(None), max_height: Cell::new(None), + tracker: Default::default(), } } @@ -158,19 +169,30 @@ impl XdgToplevel { }) } - fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), DestroyError> { - let _req: Destroy = self.xdg.surface.client.parse(self, parser)?; + fn destroy(self: &Rc, parser: MsgParser<'_, '_>) -> Result<(), DestroyError> { + let _req: Destroy = self.xdg.surface.client.parse(self.deref(), parser)?; self.destroy_node(true); self.xdg.ext.set(None); - if let Some(parent) = self.parent_node.take() { - parent.remove_child(self); - } { let mut children = self.children.borrow_mut(); + let parent = self.parent.get(); + let mut parent_children = match &parent { + Some(p) => Some(p.children.borrow_mut()), + _ => None, + }; for (_, child) in children.drain() { - child.parent.set(self.parent.get()); + child.parent.set(parent.clone()); + if let Some(parent_children) = &mut parent_children { + parent_children.insert(child.id, child); + } } } + { + if let Some(parent) = self.parent.take() { + parent.children.borrow_mut().remove(&self.id); + } + } + self.xdg.surface.client.remove_obj(self.deref())?; Ok(()) } @@ -388,9 +410,6 @@ impl Object for XdgToplevel { fn break_loops(&self) { self.destroy_node(true); - if let Some(parent) = self.parent_node.take() { - parent.remove_child(self); - } self.parent.set(None); let _children = mem::take(&mut *self.children.borrow_mut()); } @@ -411,6 +430,7 @@ impl Node for XdgToplevel { if let Some(parent) = self.parent_node.take() { if detach { parent.remove_child(self); + self.xdg.surface.client.state.tree_changed(); } } self.toplevel_history.take(); diff --git a/src/ifs/xdg_positioner.rs b/src/ifs/xdg_positioner.rs index 5ad449a9..59277272 100644 --- a/src/ifs/xdg_positioner.rs +++ b/src/ifs/xdg_positioner.rs @@ -1,5 +1,6 @@ use crate::client::{Client, ClientError}; use crate::ifs::xdg_wm_base::XdgWmBase; +use crate::leaks::Tracker; use crate::object::Object; use crate::rect::Rect; use crate::utils::buffd::MsgParser; @@ -69,6 +70,7 @@ pub struct XdgPositioner { base: Rc, client: Rc, position: RefCell, + pub tracker: Tracker, } #[derive(Copy, Clone, Debug, Default)] @@ -146,6 +148,7 @@ impl XdgPositioner { client: client.clone(), base: base.clone(), position: RefCell::new(Default::default()), + tracker: Default::default(), } } diff --git a/src/ifs/xdg_wm_base.rs b/src/ifs/xdg_wm_base.rs index 91d4e93f..4e541e90 100644 --- a/src/ifs/xdg_wm_base.rs +++ b/src/ifs/xdg_wm_base.rs @@ -2,6 +2,7 @@ use crate::client::{Client, ClientError}; use crate::globals::{Global, GlobalName}; use crate::ifs::wl_surface::xdg_surface::{XdgSurface, XdgSurfaceError}; use crate::ifs::xdg_positioner::XdgPositioner; +use crate::leaks::Tracker; use crate::object::Object; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; @@ -32,6 +33,7 @@ pub struct XdgWmBase { client: Rc, pub version: u32, pub(super) surfaces: CopyHashMap>, + pub tracker: Tracker, } impl XdgWmBaseGlobal { @@ -50,7 +52,9 @@ impl XdgWmBaseGlobal { client: client.clone(), version, surfaces: Default::default(), + tracker: Default::default(), }); + track!(client, obj); client.add_client_obj(&obj)?; Ok(()) } @@ -80,6 +84,7 @@ impl XdgWmBase { ) -> Result<(), CreatePositionerError> { let req: CreatePositioner = self.client.parse(&**self, parser)?; let pos = Rc::new(XdgPositioner::new(self, req.id, &self.client)); + track!(self.client, pos); self.client.add_client_obj(&pos)?; Ok(()) } @@ -91,6 +96,7 @@ impl XdgWmBase { let req: GetXdgSurface = self.client.parse(&**self, parser)?; let surface = self.client.lookup(req.surface)?; let xdg_surface = Rc::new(XdgSurface::new(self, req.id, &surface)); + track!(self.client, xdg_surface); self.client.add_client_obj(&xdg_surface)?; xdg_surface.install()?; self.surfaces.set(req.id, xdg_surface); diff --git a/src/ifs/zwp_linux_buffer_params_v1.rs b/src/ifs/zwp_linux_buffer_params_v1.rs index c46cb4aa..8756397f 100644 --- a/src/ifs/zwp_linux_buffer_params_v1.rs +++ b/src/ifs/zwp_linux_buffer_params_v1.rs @@ -3,6 +3,7 @@ use crate::drm::dma::{DmaBuf, DmaBufPlane}; use crate::drm::INVALID_MODIFIER; use crate::ifs::wl_buffer::WlBuffer; use crate::ifs::zwp_linux_dmabuf_v1::ZwpLinuxDmabufV1; +use crate::leaks::Tracker; use crate::object::Object; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; @@ -28,6 +29,7 @@ pub struct ZwpLinuxBufferParamsV1 { pub parent: Rc, planes: RefCell>, used: Cell, + pub tracker: Tracker, } impl ZwpLinuxBufferParamsV1 { @@ -37,6 +39,7 @@ impl ZwpLinuxBufferParamsV1 { parent: parent.clone(), planes: RefCell::new(Default::default()), used: Cell::new(false), + tracker: Default::default(), } } @@ -120,6 +123,7 @@ impl ZwpLinuxBufferParamsV1 { format, &img, )); + track!(self.parent.client, buffer); if is_client_id { self.parent.client.add_client_obj(&buffer)?; } else { diff --git a/src/ifs/zwp_linux_dmabuf_v1.rs b/src/ifs/zwp_linux_dmabuf_v1.rs index ad297732..5bcdcbd6 100644 --- a/src/ifs/zwp_linux_dmabuf_v1.rs +++ b/src/ifs/zwp_linux_dmabuf_v1.rs @@ -2,6 +2,7 @@ use crate::client::{Client, ClientError}; use crate::drm::INVALID_MODIFIER; use crate::globals::{Global, GlobalName}; use crate::ifs::zwp_linux_buffer_params_v1::ZwpLinuxBufferParamsV1; +use crate::leaks::Tracker; use crate::object::Object; use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParserError; @@ -29,7 +30,9 @@ impl ZwpLinuxDmabufV1Global { id, client: client.clone(), _version: version, + tracker: Default::default(), }); + track!(client, obj); client.add_client_obj(&obj)?; if let Some(ctx) = client.state.render_ctx.get() { let formats = ctx.formats(); @@ -68,6 +71,7 @@ pub struct ZwpLinuxDmabufV1 { id: ZwpLinuxDmabufV1Id, pub client: Rc, _version: u32, + pub tracker: Tracker, } impl ZwpLinuxDmabufV1 { @@ -96,6 +100,7 @@ impl ZwpLinuxDmabufV1 { fn create_params(self: &Rc, parser: MsgParser<'_, '_>) -> Result<(), CreateParamsError> { let req: CreateParams = self.client.parse(&**self, parser)?; let params = Rc::new(ZwpLinuxBufferParamsV1::new(req.params_id, self)); + track!(self.client, params); self.client.add_client_obj(¶ms)?; Ok(()) } diff --git a/src/ifs/zxdg_decoration_manager_v1.rs b/src/ifs/zxdg_decoration_manager_v1.rs index 4c2cd8c4..e874c759 100644 --- a/src/ifs/zxdg_decoration_manager_v1.rs +++ b/src/ifs/zxdg_decoration_manager_v1.rs @@ -1,6 +1,7 @@ use crate::client::{Client, ClientError}; use crate::globals::{Global, GlobalName}; use crate::ifs::zxdg_toplevel_decoration_v1::ZxdgToplevelDecorationV1; +use crate::leaks::Tracker; use crate::object::Object; use crate::utils::buffd::{MsgParser, MsgParserError}; use crate::wire::zxdg_decoration_manager_v1::*; @@ -26,7 +27,9 @@ impl ZxdgDecorationManagerV1Global { id, client: client.clone(), _version: version, + tracker: Default::default(), }); + track!(client, obj); client.add_client_obj(&obj)?; Ok(()) } @@ -54,6 +57,7 @@ pub struct ZxdgDecorationManagerV1 { id: ZxdgDecorationManagerV1Id, client: Rc, _version: u32, + tracker: Tracker, } impl ZxdgDecorationManagerV1 { @@ -70,6 +74,7 @@ impl ZxdgDecorationManagerV1 { let req: GetToplevelDecoration = self.client.parse(self, parser)?; let tl = self.client.lookup(req.toplevel)?; let obj = Rc::new(ZxdgToplevelDecorationV1::new(req.id, &self.client, &tl)); + track!(self.client, obj); self.client.add_client_obj(&obj)?; obj.do_send_configure(); Ok(()) diff --git a/src/ifs/zxdg_toplevel_decoration_v1.rs b/src/ifs/zxdg_toplevel_decoration_v1.rs index 68c337d9..59f213c4 100644 --- a/src/ifs/zxdg_toplevel_decoration_v1.rs +++ b/src/ifs/zxdg_toplevel_decoration_v1.rs @@ -1,5 +1,6 @@ use crate::client::{Client, ClientError}; use crate::ifs::wl_surface::xdg_surface::xdg_toplevel::{Decoration, XdgToplevel}; +use crate::leaks::Tracker; use crate::object::Object; use crate::utils::buffd::{MsgParser, MsgParserError}; use crate::wire::zxdg_toplevel_decoration_v1::*; @@ -14,6 +15,7 @@ pub struct ZxdgToplevelDecorationV1 { pub id: ZxdgToplevelDecorationV1Id, pub client: Rc, pub toplevel: Rc, + pub tracker: Tracker, } impl ZxdgToplevelDecorationV1 { @@ -26,6 +28,7 @@ impl ZxdgToplevelDecorationV1 { id, client: client.clone(), toplevel: toplevel.clone(), + tracker: Default::default(), } } diff --git a/src/leaks.rs b/src/leaks.rs new file mode 100644 index 00000000..a6db2db5 --- /dev/null +++ b/src/leaks.rs @@ -0,0 +1,267 @@ +pub use leaks::*; + +#[cfg(not(feature = "rc_tracking"))] +#[macro_use] +mod leaks { + use std::marker::PhantomData; + + pub fn init() { + // nothing + } + + pub fn log_leaked() { + // nothing + } + + pub struct Tracker { + _phantom: PhantomData, + } + + impl Default for Tracker { + fn default() -> Self { + Self { + _phantom: Default::default(), + } + } + } + + macro_rules! track { + ($client:expr, $rc:expr) => { + let _ = $rc.tracker; + } + } +} + +#[cfg(feature = "rc_tracking")] +#[macro_use] +mod leaks { + use crate::client::ClientId; + use crate::utils::ptr_ext::{MutPtrExt, PtrExt}; + use ahash::{AHashMap, AHashSet}; + use backtrace::Backtrace; + use std::alloc::{GlobalAlloc, Layout}; + use std::marker::PhantomData; + use std::{any, mem, ptr}; + use uapi::c; + + #[thread_local] + static mut MAP: *mut AHashMap = ptr::null_mut(); + + #[thread_local] + static mut ID: u64 = 0; + + pub fn init() { + unsafe { + MAP = Box::into_raw(Box::new(AHashMap::new())); + ALLOCATIONS = Box::into_raw(Box::new(AHashMap::new())); + IN_ALLOCATOR = 0; + INITIALIZED = true; + } + } + + fn log_containers(prefix: &str, allocation: &mut Allocation, offset: usize, logged: &mut AHashSet<*mut u8>) { + log::info!( + "{}Contained in allocation {:?} at offset {}. Backtrace:", + prefix, + allocation.addr, + offset + ); + allocation.backtrace.resolve(); + let bt = format!("{:?}", allocation.backtrace); + for line in bt.lines() { + log::info!("{} {}", prefix, line); + } + + if !logged.insert(allocation.addr) { + log::error!("{} LOOP", prefix); + } else { + let containers = find_allocations_pointing_to(allocation.addr); + if containers.is_empty() { + log::error!("{} NO REFERENCES", prefix); + } + let new_prefix = format!("{} ", prefix); + for (mut allocation, offset) in containers { + log_containers(&new_prefix, &mut allocation, offset, logged); + } + logged.remove(&allocation.addr); + } + } + + pub fn log_leaked() { + unsafe { + IN_ALLOCATOR += 1; + let mut map: AHashMap> = AHashMap::new(); + for (id, obj) in MAP.deref_mut().drain() { + map.entry(obj.client).or_default().push((id, obj)); + } + for (_, mut objs) in map.drain() { + if objs.len() == 0 { + continue; + } + objs.sort_by_key(|o| o.0); + log::info!("Client {} leaked {} objects", objs[0].1.client, objs.len()); + for (_, obj) in objs { + let time = chrono::NaiveDateTime::from_timestamp(obj.time.0, obj.time.1); + log::info!( + " [{}] {}", + time.format("%H:%M:%S%.3f"), + obj.ty, + ); + match find_allocation_containing(obj.addr) { + Some(mut alloc) => log_containers(" ", &mut alloc, 0, &mut AHashSet::new()), + _ => log::error!(" Not contained in any allocation??"), + } + } + } + IN_ALLOCATOR -= 1; + } + } + + #[derive(Copy, Clone)] + struct Tracked { + addr: usize, + client: ClientId, + ty: &'static str, + time: (i64, u32), + } + + pub struct Tracker { + id: u64, + _phantom: PhantomData, + } + + impl Default for Tracker { + fn default() -> Self { + Self { + id: unsafe { + let id = ID; + ID += 1; + id + }, + _phantom: Default::default(), + } + } + } + + impl Tracker { + pub fn register(&self, client_id: ClientId) { + unsafe { + let mut time = c::timespec { + tv_sec: 0, + tv_nsec: 0, + }; + uapi::clock_gettime(c::CLOCK_REALTIME, &mut time).unwrap(); + IN_ALLOCATOR += 1; + MAP.deref_mut().insert( + self.id, + Tracked { + addr: self as *const _ as usize, + client: client_id, + ty: any::type_name::(), + time: (time.tv_sec as i64, time.tv_nsec as u32), + }, + ); + IN_ALLOCATOR -= 1; + } + } + } + + impl Drop for Tracker { + fn drop(&mut self) { + unsafe { + MAP.deref_mut().remove(&self.id); + } + } + } + + macro_rules! track { + ($client:expr, $rc:expr) => { + ($rc) + .tracker + .register($client.id); + }; + } + + struct TracingAllocator; + + #[global_allocator] + static GLOBAL: TracingAllocator = TracingAllocator; + + #[derive(Clone)] + struct Allocation { + pub addr: *mut u8, + pub len: usize, + pub backtrace: Backtrace, + } + + #[thread_local] + static mut ALLOCATIONS: *mut AHashMap<*mut u8, Allocation> = ptr::null_mut(); + + #[thread_local] + static mut IN_ALLOCATOR: u32 = 1; + + #[thread_local] + static mut INITIALIZED: bool = false; + + unsafe impl GlobalAlloc for TracingAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + let res = c::calloc(layout.size(), 1) as *mut u8; + if IN_ALLOCATOR == 0 { + IN_ALLOCATOR = 1; + ALLOCATIONS.deref_mut().insert( + res, + Allocation { + addr: res, + len: layout.size(), + backtrace: Backtrace::new_unresolved(), + }, + ); + IN_ALLOCATOR = 0; + } + res + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + if INITIALIZED { + ALLOCATIONS.deref_mut().remove(&ptr); + } + c::memset(ptr as _, 0, layout.size()); + c::free(ptr as _); + } + } + + fn find_allocations_pointing_to(addr: *mut u8) -> Vec<(Allocation, usize)> { + unsafe { + IN_ALLOCATOR += 1; + let mut res = vec![]; + for allocation in ALLOCATIONS.deref().values() { + let num = allocation.len / mem::size_of::(); + let elements = std::slice::from_raw_parts(allocation.addr as *const *mut u8, num); + for (offset, pos) in elements.iter().enumerate() { + if *pos == addr { + res.push((allocation.clone(), offset * mem::size_of::())); + break; + } + } + } + IN_ALLOCATOR -= 1; + res + } + } + + fn find_allocation_containing(addr: usize) -> Option { + unsafe { + IN_ALLOCATOR += 1; + let mut res = None; + for allocation in ALLOCATIONS.deref().values() { + let aaddr = allocation.addr as usize; + if aaddr <= addr && addr < aaddr + allocation.len { + res = Some(allocation.clone()); + break; + } + } + IN_ALLOCATOR -= 1; + res + } + } +} diff --git a/src/main.rs b/src/main.rs index 9cf3ab66..3a2b1d94 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,11 @@ -#![feature(c_variadic, thread_local, label_break_value, ptr_metadata)] +#![feature( + c_variadic, + thread_local, + label_break_value, + ptr_metadata, + linkage, + const_type_name +)] #![allow( clippy::len_zero, clippy::needless_lifetimes, @@ -32,14 +39,18 @@ use crate::wheel::WheelError; use acceptor::Acceptor; use async_engine::AsyncEngine; use event_loop::EventLoop; +use isnt::std_1::primitive::IsntMutPtrExt; use log::LevelFilter; use std::cell::Cell; +use std::ops::Deref; use std::rc::Rc; use thiserror::Error; use wheel::Wheel; #[macro_use] mod macros; +#[macro_use] +pub mod leaks; mod acceptor; mod async_engine; mod backend; @@ -70,6 +81,15 @@ mod wire; mod xkbcommon; fn main() { + unsafe { + extern "C" { + #[linkage = "extern_weak"] + static BYTEHOUND_REACHED_MAIN: *mut bool; + } + if BYTEHOUND_REACHED_MAIN.is_not_null() { + *BYTEHOUND_REACHED_MAIN = true; + } + } env_logger::builder() .filter_level(LevelFilter::Info) .filter_level(LevelFilter::Debug) @@ -102,6 +122,7 @@ enum MainError { } fn main_() -> Result<(), MainError> { + leaks::init(); render::init()?; clientmem::init()?; let el = EventLoop::new()?; @@ -136,5 +157,10 @@ fn main_() -> Result<(), MainError> { Acceptor::install(&state)?; let _backend = XorgBackend::new(&state)?; el.run()?; + state.clients.clear(); + for (_, seat) in state.globals.seats.lock().deref() { + seat.clear(); + } + leaks::log_leaked(); Ok(()) } diff --git a/src/tree/container.rs b/src/tree/container.rs index 8051b4cc..a39e2ab2 100644 --- a/src/tree/container.rs +++ b/src/tree/container.rs @@ -10,6 +10,7 @@ use crate::utils::linkedlist::{LinkedList, LinkedNode, NodeRef}; use crate::{NumCell, State}; use ahash::AHashMap; use std::cell::{Cell, RefCell}; +use std::fmt::{Debug, Formatter}; use std::mem; use std::ops::DerefMut; use std::rc::Rc; @@ -55,6 +56,12 @@ pub struct ContainerNode { seats: RefCell>, } +impl Debug for ContainerNode { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ContainerNode").finish_non_exhaustive() + } +} + pub struct ContainerChild { pub node: Rc, pub body: Cell, @@ -318,7 +325,7 @@ impl ContainerNode { dist_left, dist_right, } => { - let prev = op.child.prev(); + let prev = op.child.prev().unwrap(); let prev_body = prev.body.get(); let child_body = op.child.body.get(); let (prev_factor, child_factor) = match self.split.get() { @@ -458,7 +465,7 @@ impl Node for ContainerNode { if seat_data.x < body.x2() { let op = if seat_data.x < body.x1() { SeatOpKind::Resize { - dist_left: seat_data.x - child.prev().body.get().x2(), + dist_left: seat_data.x - child.prev().unwrap().body.get().x2(), dist_right: body.x1() - seat_data.x, } } else { @@ -473,7 +480,7 @@ impl Node for ContainerNode { if seat_data.y < body.y1() { let op = if seat_data.y < body.y1() - CONTAINER_TITLE_HEIGHT { SeatOpKind::Resize { - dist_left: seat_data.y - child.prev().body.get().y2(), + dist_left: seat_data.y - child.prev().unwrap().body.get().y2(), dist_right: body.y1() - seat_data.y, } } else { @@ -528,6 +535,7 @@ impl Node for ContainerNode { }; let num_children = self.num_children.fetch_sub(1) - 1; if num_children == 0 { + self.seats.borrow_mut().clear(); self.parent.get().remove_child(self); return; } diff --git a/src/tree/mod.rs b/src/tree/mod.rs index e7ce8296..f05c842f 100644 --- a/src/tree/mod.rs +++ b/src/tree/mod.rs @@ -14,7 +14,7 @@ use crate::xkbcommon::ModifierState; use crate::NumCell; pub use container::*; use std::cell::{Cell, RefCell}; -use std::fmt::Display; +use std::fmt::{Debug, Display, Formatter}; use std::ops::Deref; use std::rc::Rc; pub use workspace::*; @@ -306,6 +306,12 @@ pub struct OutputNode { pub seat_state: NodeSeatState, } +impl Debug for OutputNode { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("OutputNode").finish_non_exhaustive() + } +} + impl Node for OutputNode { fn id(&self) -> NodeId { self.id.into() @@ -375,6 +381,12 @@ pub struct FloatNode { pub seat_state: NodeSeatState, } +impl Debug for FloatNode { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FloatNode").finish_non_exhaustive() + } +} + impl Node for FloatNode { fn id(&self) -> NodeId { self.id.into() diff --git a/src/tree/workspace.rs b/src/tree/workspace.rs index 59461ea3..e32e8086 100644 --- a/src/tree/workspace.rs +++ b/src/tree/workspace.rs @@ -6,6 +6,7 @@ use crate::tree::container::ContainerNode; use crate::tree::{FindTreeResult, FoundNode, Node, NodeId, OutputNode}; use crate::utils::clonecell::CloneCell; use crate::utils::linkedlist::LinkedList; +use std::fmt::Debug; use std::rc::Rc; tree_id!(WorkspaceNodeId); diff --git a/src/utils/asyncevent.rs b/src/utils/asyncevent.rs index 53e31589..20b1faa5 100644 --- a/src/utils/asyncevent.rs +++ b/src/utils/asyncevent.rs @@ -11,6 +11,10 @@ pub struct AsyncEvent { } impl AsyncEvent { + pub fn clear(&self) { + self.waker.take(); + } + pub fn trigger(&self) { self.triggers.fetch_add(1); if let Some(waker) = self.waker.take() { diff --git a/src/utils/buffd/buf_out.rs b/src/utils/buffd/buf_out.rs index 0c688506..cfd3483e 100644 --- a/src/utils/buffd/buf_out.rs +++ b/src/utils/buffd/buf_out.rs @@ -65,9 +65,7 @@ impl OutBufferSwapchain { pub fn commit(&mut self) { if self.cur.write_pos > 0 { - let new = self.free.pop().unwrap_or_else(|| { - Default::default() - }); + let new = self.free.pop().unwrap_or_else(|| Default::default()); let old = mem::replace(&mut self.cur, new); self.pending.push_back(old); } diff --git a/src/utils/clonecell.rs b/src/utils/clonecell.rs index 8071fb7e..9b7f60b4 100644 --- a/src/utils/clonecell.rs +++ b/src/utils/clonecell.rs @@ -1,6 +1,7 @@ use crate::utils::linkedlist::NodeRef; use crate::utils::ptr_ext::{MutPtrExt, PtrExt}; use std::cell::UnsafeCell; +use std::fmt::{Debug, Formatter}; use std::mem; use std::rc::Rc; @@ -8,6 +9,12 @@ pub struct CloneCell { data: UnsafeCell, } +impl Debug for CloneCell { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + unsafe { self.data.get().deref().fmt(f) } + } +} + impl CloneCell { pub fn new(t: T) -> Self { Self { diff --git a/src/utils/linkedlist.rs b/src/utils/linkedlist.rs index 281f7665..68f28186 100644 --- a/src/utils/linkedlist.rs +++ b/src/utils/linkedlist.rs @@ -1,7 +1,7 @@ use crate::utils::ptr_ext::PtrExt; use crate::NumCell; use std::cell::Cell; -use std::mem::MaybeUninit; +use std::fmt::{Debug, Formatter}; use std::ops::Deref; use std::ptr::NonNull; @@ -9,6 +9,12 @@ pub struct LinkedList { root: LinkedNode, } +impl Debug for LinkedList { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_list().entries(self.iter()).finish() + } +} + impl Default for LinkedList { fn default() -> Self { Self::new() @@ -18,10 +24,10 @@ impl Default for LinkedList { impl LinkedList { pub fn new() -> Self { let node = Box::into_raw(Box::new(NodeData { - rc: NumCell::new(3), + rc: NumCell::new(1), prev: Cell::new(NonNull::dangling()), next: Cell::new(NonNull::dangling()), - data: MaybeUninit::uninit(), + data: None, })); unsafe { node.deref().prev.set(NonNull::new_unchecked(node)); @@ -151,11 +157,17 @@ pub struct LinkedNode { data: NonNull>, } +impl Debug for LinkedNode { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + unsafe { self.data.as_ref().data.as_ref().unwrap_unchecked().fmt(f) } + } +} + impl Deref for LinkedNode { type Target = T; fn deref(&self) -> &Self::Target { - unsafe { self.data.as_ref().data.assume_init_ref() } + unsafe { self.data.as_ref().data.as_ref().unwrap_unchecked() } } } @@ -163,11 +175,17 @@ pub struct NodeRef { data: NonNull>, } +impl Debug for NodeRef { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + unsafe { self.data.as_ref().data.as_ref().unwrap_unchecked().fmt(f) } + } +} + impl Deref for NodeRef { type Target = T; fn deref(&self) -> &Self::Target { - unsafe { self.data.as_ref().data.assume_init_ref() } + unsafe { self.data.as_ref().data.as_ref().unwrap_unchecked() } } } @@ -197,22 +215,16 @@ impl NodeRef { unsafe { append(self.data, t) } } - pub fn prev(&self) -> NodeRef { + pub fn prev(&self) -> Option> { unsafe { let data = self.data.as_ref(); let other = data.prev.get(); - other.as_ref().rc.fetch_add(1); - NodeRef { data: other } - } - } - - #[allow(dead_code)] - pub fn next(&self) -> NodeRef { - unsafe { - let data = self.data.as_ref(); - let other = data.next.get(); - other.as_ref().rc.fetch_add(1); - NodeRef { data: other } + if other.as_ref().data.is_some() { + other.as_ref().rc.fetch_add(1); + Some(NodeRef { data: other }) + } else { + None + } } } } @@ -221,7 +233,7 @@ struct NodeData { rc: NumCell, prev: Cell>>, next: Cell>>, - data: MaybeUninit, + data: Option, } unsafe fn dec_ref_count(slf: NonNull>, n: usize) { @@ -237,8 +249,10 @@ impl Drop for LinkedNode { let data = self.data.as_ref(); data.prev.get().as_ref().next.set(data.next.get()); data.next.get().as_ref().prev.set(data.prev.get()); + data.prev.set(self.data); + data.next.set(self.data); } - dec_ref_count(self.data, 3); + dec_ref_count(self.data, 1); } } } @@ -263,10 +277,10 @@ impl LinkedNode { unsafe fn prepend(data: NonNull>, t: T) -> LinkedNode { let dref = data.as_ref(); let node = NonNull::new_unchecked(Box::into_raw(Box::new(NodeData { - rc: NumCell::new(3), + rc: NumCell::new(1), prev: Cell::new(dref.prev.get()), next: Cell::new(data), - data: MaybeUninit::new(t), + data: Some(t), }))); dref.prev.get().as_ref().next.set(node); dref.prev.set(node); @@ -276,10 +290,10 @@ unsafe fn prepend(data: NonNull>, t: T) -> LinkedNode { unsafe fn append(data: NonNull>, t: T) -> LinkedNode { let dref = data.as_ref(); let node = NonNull::new_unchecked(Box::into_raw(Box::new(NodeData { - rc: NumCell::new(3), + rc: NumCell::new(1), prev: Cell::new(data), next: Cell::new(dref.next.get()), - data: MaybeUninit::new(t), + data: Some(t), }))); dref.next.get().as_ref().prev.set(node); dref.next.set(node); diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 67e2f562..77112db6 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -7,7 +7,6 @@ pub mod debug_fn; pub mod errorfmt; pub mod linkedlist; pub mod numcell; -pub mod oneshot; pub mod ptr_ext; pub mod queue; pub mod smallmap; diff --git a/src/utils/numcell.rs b/src/utils/numcell.rs index 2a36a517..8be91b98 100644 --- a/src/utils/numcell.rs +++ b/src/utils/numcell.rs @@ -1,4 +1,5 @@ use std::cell::Cell; +use std::fmt::{Debug, Formatter}; use std::ops::{Add, BitAnd, BitOr, Sub}; #[derive(Default)] @@ -6,6 +7,12 @@ pub struct NumCell { t: Cell, } +impl Debug for NumCell { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.t.get().fmt(f) + } +} + impl NumCell { #[inline(always)] pub fn new(t: T) -> Self { diff --git a/src/utils/oneshot.rs b/src/utils/oneshot.rs deleted file mode 100644 index 9554ee87..00000000 --- a/src/utils/oneshot.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::cell::Cell; -use std::future::Future; -use std::pin::Pin; -use std::rc::Rc; -use std::task::{Context, Poll, Waker}; - -pub fn oneshot() -> (OneshotTx, OneshotRx) { - let os = Rc::new(Oneshot { - data: Cell::new(None), - waiter: Cell::new(None), - }); - (OneshotTx { data: os.clone() }, OneshotRx { data: os }) -} - -struct Oneshot { - data: Cell>, - waiter: Cell>, -} - -pub struct OneshotTx { - data: Rc>, -} - -pub struct OneshotRx { - data: Rc>, -} - -impl OneshotTx { - pub fn send(self, t: T) { - self.data.data.set(Some(t)); - if let Some(waiter) = self.data.waiter.replace(None) { - waiter.wake(); - } - } -} - -impl Future for OneshotRx { - type Output = T; - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { - if let Some(data) = self.data.data.replace(None) { - Poll::Ready(data) - } else { - self.data.waiter.set(Some(cx.waker().clone())); - Poll::Pending - } - } -} diff --git a/src/utils/queue.rs b/src/utils/queue.rs index 29456a5d..902fdd27 100644 --- a/src/utils/queue.rs +++ b/src/utils/queue.rs @@ -35,6 +35,7 @@ impl AsyncQueue { pub fn clear(&self) { mem::take(&mut *self.data.borrow_mut()); + mem::take(&mut *self.waiters.borrow_mut()); } } diff --git a/src/utils/smallmap.rs b/src/utils/smallmap.rs index 9468e8a9..d72ae33f 100644 --- a/src/utils/smallmap.rs +++ b/src/utils/smallmap.rs @@ -2,12 +2,23 @@ use crate::utils::clonecell::UnsafeCellCloneSafe; use crate::utils::ptr_ext::{MutPtrExt, PtrExt}; use smallvec::SmallVec; use std::cell::UnsafeCell; +use std::fmt::{Debug, Formatter}; use std::mem; pub struct SmallMap { m: UnsafeCell>, } +impl Debug for SmallMap { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + unsafe { + f.debug_map() + .entries(self.m.get().deref().iter().map(|e| (&e.0, &e.1))) + .finish() + } + } +} + impl Default for SmallMap { fn default() -> Self { Self { diff --git a/todo.md b/todo.md index 9e560dcf..d8879a52 100644 --- a/todo.md +++ b/todo.md @@ -9,7 +9,6 @@ - Workspaces - Float moving - Highlighting active -- dnd - presentation time - viewporter - session lock @@ -19,3 +18,4 @@ - Container resizing - clipboard - primary selection +- dnd