diff --git a/src/async_engine/ae_task.rs b/src/async_engine/ae_task.rs index 4dfee337..0f102814 100644 --- a/src/async_engine/ae_task.rs +++ b/src/async_engine/ae_task.rs @@ -158,10 +158,21 @@ impl> Task { let task = data as *const Self; if run { task.deref().run(); + } else { + Self::task_runnable_dropped(task); } Self::dec_ref_count(task); } + #[cold] + unsafe fn task_runnable_dropped(task: *const Self) { + let task = task.deref(); + task.state.and_assign(!RUNNING); + if task.state.get() & CANCELLED != 0 { + task.drop_data(); + } + } + unsafe fn dec_ref_count(slf: *const Self) { if slf.deref().ref_count.fetch_sub(1) == 1 { Box::from_raw(slf as *mut Self); diff --git a/src/backends/metal.rs b/src/backends/metal.rs index 0d59e19e..00b098af 100644 --- a/src/backends/metal.rs +++ b/src/backends/metal.rs @@ -46,7 +46,6 @@ use { ffi::{CStr, CString}, fmt::{Debug, Formatter}, future::pending, - mem, rc::Rc, }, thiserror::Error, @@ -293,14 +292,6 @@ struct MetalInputDevice { transform_matrix: Cell>, } -impl Drop for MetalInputDevice { - fn drop(&mut self) { - if let Some(fd) = self.fd.take() { - mem::forget(fd); - } - } -} - #[derive(Clone)] enum MetalDevice { Input(Rc), diff --git a/src/compositor.rs b/src/compositor.rs index 07f074dc..c69977b2 100644 --- a/src/compositor.rs +++ b/src/compositor.rs @@ -48,7 +48,9 @@ pub const MAX_EXTENTS: i32 = (1 << 22) - 1; pub fn start_compositor(global: GlobalArgs, args: RunArgs) { let forker = create_forker(); let logger = Logger::install_compositor(global.log_level.into()); - if let Err(e) = start_compositor2(forker, Some(logger.clone()), args, None) { + let res = start_compositor2(Some(forker), Some(logger.clone()), args, None); + leaks::log_leaked(); + if let Err(e) = res { let e = ErrorFmt(e); log::error!("A fatal error occurred: {}", e); eprintln!("A fatal error occurred: {}", e); @@ -60,8 +62,9 @@ pub fn start_compositor(global: GlobalArgs, args: RunArgs) { #[cfg(feature = "it")] pub fn start_compositor_for_test(future: TestFuture) -> Result<(), CompositorError> { - let forker = create_forker(); - start_compositor2(forker, None, RunArgs::default(), Some(future)) + let res = start_compositor2(None, None, RunArgs::default(), Some(future)); + leaks::log_leaked(); + res } fn create_forker() -> Rc { @@ -99,7 +102,7 @@ const STATIC_VARS: &[(&str, &str)] = &[ pub type TestFuture = Box) -> Box>>; fn start_compositor2( - forker: Rc, + forker: Option>, logger: Option>, run_args: RunArgs, test_future: Option, @@ -183,28 +186,20 @@ fn start_compositor2( }); state.tracker.register(ClientId::from_raw(0)); create_dummy_output(&state); - let (acceptor, acceptor_future) = Acceptor::install(&state)?; - forker.install(&state); - forker.setenv( - WAYLAND_DISPLAY.as_bytes(), - acceptor.socket_name().as_bytes(), - ); - for (key, val) in STATIC_VARS { - forker.setenv(key.as_bytes(), val.as_bytes()); + let (acceptor, _acceptor_future) = Acceptor::install(&state)?; + if let Some(forker) = forker { + forker.install(&state); + forker.setenv( + WAYLAND_DISPLAY.as_bytes(), + acceptor.socket_name().as_bytes(), + ); + for (key, val) in STATIC_VARS { + forker.setenv(key.as_bytes(), val.as_bytes()); + } } - let compositor = engine.spawn(start_compositor3(state.clone(), test_future)); + let _compositor = engine.spawn(start_compositor3(state.clone(), test_future)); ring.run()?; - drop(compositor); - drop(acceptor_future); - drop(acceptor); - drop(forker); - engine.clear(); state.clear(); - for (_, seat) in state.globals.seats.lock().deref() { - seat.clear(); - } - drop(state); - leaks::log_leaked(); Ok(()) } @@ -352,6 +347,7 @@ fn create_dummy_output(state: &Rc) { connected: Cell::new(true), name: "Dummy".to_string(), drm_dev: None, + async_event: Default::default(), }), 0, &backend::Mode { diff --git a/src/forker.rs b/src/forker.rs index b97545d9..1433c3f1 100644 --- a/src/forker.rs +++ b/src/forker.rs @@ -77,6 +77,7 @@ impl ForkerProxy { self.task_in.take(); self.task_out.take(); self.task_proc.take(); + self.outgoing.clear(); } pub fn create() -> Result { diff --git a/src/ifs/wl_seat/event_handling.rs b/src/ifs/wl_seat/event_handling.rs index 85f510e4..14153c6a 100644 --- a/src/ifs/wl_seat/event_handling.rs +++ b/src/ifs/wl_seat/event_handling.rs @@ -154,6 +154,13 @@ impl NodeSeatState { self.dnd_targets.remove(&seat.id); self.pointer_grabs.remove(&seat.id); } + + pub fn clear(&self) { + self.kb_foci.clear(); + self.pointer_foci.clear(); + self.dnd_targets.clear(); + self.pointer_grabs.clear(); + } } impl WlSeatGlobal { diff --git a/src/ifs/wl_surface.rs b/src/ifs/wl_surface.rs index 75959000..88cd5311 100644 --- a/src/ifs/wl_surface.rs +++ b/src/ifs/wl_surface.rs @@ -697,6 +697,8 @@ impl Object for WlSurface { self.buffer.set(None); self.toplevel.set(None); self.idle_inhibitors.clear(); + self.pending.presentation_feedback.borrow_mut().clear(); + self.presentation_feedback.borrow_mut().clear(); } } diff --git a/src/ifs/wl_surface/wl_subsurface.rs b/src/ifs/wl_surface/wl_subsurface.rs index 831155d6..d0ffd907 100644 --- a/src/ifs/wl_surface/wl_subsurface.rs +++ b/src/ifs/wl_surface/wl_subsurface.rs @@ -140,6 +140,7 @@ impl WlSubsurface { fn destroy(&self, parser: MsgParser<'_, '_>) -> Result<(), WlSubsurfaceError> { let _req: Destroy = self.surface.client.parse(self, parser)?; self.surface.unset_ext(); + *self.pending.node.borrow_mut() = None; *self.node.borrow_mut() = None; { let mut children = self.parent.children.borrow_mut(); @@ -257,6 +258,11 @@ impl Object for WlSubsurface { fn num_requests(&self) -> u32 { SET_DESYNC + 1 } + + fn break_loops(&self) { + *self.pending.node.borrow_mut() = None; + *self.node.borrow_mut() = None; + } } simple_add_obj!(WlSubsurface); diff --git a/src/it.rs b/src/it.rs index c727523a..49a37f40 100644 --- a/src/it.rs +++ b/src/it.rs @@ -7,6 +7,7 @@ use { testrun::TestRun, tests::TestCase, }, + leaks, utils::{errorfmt::ErrorFmt, num_cpus::num_cpus}, }, ahash::AHashMap, @@ -45,6 +46,7 @@ mod tests; const SINGLE_THREAD: bool = false; pub fn run_tests() { + leaks::init(); test_logger::install(); test_logger::set_level(Level::Trace); let it_run = Arc::new(ItRun { diff --git a/src/leaks.rs b/src/leaks.rs index dbbb8e6d..97072151 100644 --- a/src/leaks.rs +++ b/src/leaks.rs @@ -42,7 +42,10 @@ mod leaks { use { crate::{ client::ClientId, - utils::ptr_ext::{MutPtrExt, PtrExt}, + utils::{ + ptr_ext::{MutPtrExt, PtrExt}, + windows::WindowsExt, + }, }, ahash::{AHashMap, AHashSet}, backtrace::Backtrace, @@ -63,6 +66,9 @@ mod leaks { pub fn init() { unsafe { + if INITIALIZED { + return; + } MAP = Box::into_raw(Box::new(AHashMap::new())); ALLOCATIONS = Box::into_raw(Box::new(AHashMap::new())); IN_ALLOCATOR = 0; @@ -93,7 +99,47 @@ mod leaks { } else { let containers = find_allocations_pointing_to(allocation.addr); if containers.is_empty() { - log::error!("{} NO REFERENCES", prefix); + let mut frames = vec![]; + backtrace::trace(|frame| { + frames.push((frame.ip() as usize, frame.sp() as usize)); + true + }); + let mut frames2 = vec![]; + for [l, r] in frames.array_windows_ext::<2>() { + frames2.push((l.0, l.1, r.1)); + } + let mut referenced_on_stack = false; + for (ip, lo, hi) in frames2 { + if lo % 8 != 0 { + log::error!("lo % 8 != 0"); + } + let slice = unsafe { + std::slice::from_raw_parts( + lo as *const *mut u8, + (hi - lo) / mem::size_of::(), + ) + }; + for addr in slice { + if *addr == allocation.addr { + let mut name = String::new(); + backtrace::resolve(ip as _, |sym| { + let symname = match sym.name() { + Some(s) => s.to_string(), + _ => String::new(), + }; + name = + format!("{} {:?}:{:?}", symname, sym.filename(), sym.lineno()) + }); + if !name.starts_with("jay::leaks::") { + log::info!("{} REFERENCED ON THE STACK: {}", prefix, name); + referenced_on_stack = true; + } + } + } + } + if !referenced_on_stack { + log::error!("{} NO REFERENCES", prefix); + } } let new_prefix = format!("{} ", prefix); for (mut allocation, offset) in containers { @@ -133,6 +179,19 @@ mod leaks { IN_ALLOCATOR -= 1; } } + // + // pub fn log_allocations(w: &mut dyn Write) { + // log::info!("remaining allocations:"); + // unsafe { + // IN_ALLOCATOR += 1; + // for (_, a) in ALLOCATIONS.deref() { + // let mut bt = a.backtrace.clone(); + // bt.resolve(); + // write!(w, "[{:?}, {:?}), allocated at\n{:?}", a.addr, a.addr.add(a.len), bt); + // } + // IN_ALLOCATOR -= 1; + // } + // } #[derive(Copy, Clone)] struct Tracked { @@ -225,6 +284,7 @@ mod leaks { backtrace: Backtrace::new_unresolved(), }, ); + // log::info!("allocated [0x{:x}, 0x{:x})", res as usize, res as usize + layout.size()); IN_ALLOCATOR = 0; } res diff --git a/src/state.rs b/src/state.rs index fb81b950..a0e75fab 100644 --- a/src/state.rs +++ b/src/state.rs @@ -161,6 +161,7 @@ pub struct InputDeviceData { pub handler: SpawnedFuture<()>, pub id: InputDeviceId, pub data: Rc, + pub async_event: Rc, } pub struct DeviceHandlerData { @@ -174,6 +175,7 @@ pub struct ConnectorData { pub connected: Cell, pub name: String, pub drm_dev: Option>, + pub async_event: Rc, } pub struct OutputData { @@ -491,20 +493,19 @@ impl State { if let Some(forker) = self.forker.set(None) { forker.clear(); } - if let Some(output) = self.dummy_output.set(None) { - output.clear(); - } self.acceptor.set(None); self.backend.set(Rc::new(DummyBackend)); self.run_toplevel.clear(); self.xwayland.handler.borrow_mut().take(); self.xwayland.queue.clear(); self.idle.inhibitors.clear(); + self.idle.change.clear(); for (_, drm_dev) in self.drm_devs.lock().drain() { drm_dev.handler.take(); } for (_, connector) in self.connectors.lock().drain() { connector.handler.take(); + connector.async_event.clear(); } for (_, output) in self.outputs.lock().drain() { output.node.clear(); @@ -515,17 +516,24 @@ impl State { self.pending_float_layout.clear(); self.pending_float_titles.clear(); self.slow_clients.clear(); - self.input_device_handlers.borrow_mut().clear(); + for (_, h) in self.input_device_handlers.borrow_mut().drain() { + h.async_event.clear(); + } self.backend_events.clear(); self.workspaces.clear(); - self.globals.clear(); - self.render_ctx.set(None); - self.root.clear(); { let seats = mem::take(self.globals.seats.lock().deref_mut()); for seat in seats.values() { seat.clear(); } } + self.globals.clear(); + self.render_ctx.set(None); + self.root.clear(); + if let Some(output) = self.dummy_output.set(None) { + output.clear(); + } + self.wheel.clear(); + self.eng.clear(); } } diff --git a/src/tasks/connector.rs b/src/tasks/connector.rs index 4bd19aa6..14929074 100644 --- a/src/tasks/connector.rs +++ b/src/tasks/connector.rs @@ -27,6 +27,7 @@ pub fn handle(state: &Rc, connector: &Rc) { connected: Cell::new(false), name: connector.kernel_id().to_string(), drm_dev: drm_dev.clone(), + async_event: Rc::new(AsyncEvent::default()), }); if let Some(dev) = drm_dev { dev.connectors.set(id, data.clone()); @@ -51,9 +52,8 @@ struct ConnectorHandler { impl ConnectorHandler { async fn handle(self) { - let ae = Rc::new(AsyncEvent::default()); { - let ae = ae.clone(); + let ae = self.data.async_event.clone(); self.data.connector.on_change(Rc::new(move || ae.trigger())); } if let Some(config) = self.state.config.get() { @@ -63,11 +63,11 @@ impl ConnectorHandler { while let Some(event) = self.data.connector.event() { match event { ConnectorEvent::Removed => break 'outer, - ConnectorEvent::Connected(mi) => self.handle_connected(&ae, mi).await, + ConnectorEvent::Connected(mi) => self.handle_connected(mi).await, _ => unreachable!(), } } - ae.triggered().await; + self.data.async_event.triggered().await; } if let Some(config) = self.state.config.get() { config.del_connector(self.id); @@ -76,7 +76,7 @@ impl ConnectorHandler { self.state.connectors.remove(&self.id); } - async fn handle_connected(&self, ae: &Rc, info: MonitorInfo) { + async fn handle_connected(&self, info: MonitorInfo) { log::info!("Connector {} connected", self.data.connector.kernel_id()); self.data.connected.set(true); let name = self.state.globals.name(); @@ -151,7 +151,7 @@ impl ConnectorHandler { _ => unreachable!(), } } - ae.triggered().await; + self.data.async_event.triggered().await; } log::info!("Connector {} disconnected", self.data.connector.kernel_id()); if let Some(config) = self.state.config.get() { diff --git a/src/tasks/input_device.rs b/src/tasks/input_device.rs index 3c9e80e1..1b304b2e 100644 --- a/src/tasks/input_device.rs +++ b/src/tasks/input_device.rs @@ -12,10 +12,12 @@ pub fn handle(state: &Rc, dev: Rc) { seat: Default::default(), device: dev.clone(), }); + let ae = Rc::new(AsyncEvent::default()); let oh = DeviceHandler { state: state.clone(), dev: dev.clone(), data: data.clone(), + ae: ae.clone(), }; let handler = state.eng.spawn(oh.handle()); state.input_device_handlers.borrow_mut().insert( @@ -24,21 +26,22 @@ pub fn handle(state: &Rc, dev: Rc) { handler, id: dev.id(), data, + async_event: ae, }, ); } -pub struct DeviceHandler { - pub state: Rc, - pub dev: Rc, - pub data: Rc, +struct DeviceHandler { + state: Rc, + dev: Rc, + data: Rc, + ae: Rc, } impl DeviceHandler { pub async fn handle(self) { - let ae = Rc::new(AsyncEvent::default()); { - let ae = ae.clone(); + let ae = self.ae.clone(); self.dev.on_change(Rc::new(move || ae.trigger())); } if let Some(config) = self.state.config.get() { @@ -63,7 +66,7 @@ impl DeviceHandler { // nothing } } - ae.triggered().await; + self.ae.triggered().await; } if let Some(config) = self.state.config.get() { config.del_input_device(self.dev.id()); diff --git a/src/tree/display.rs b/src/tree/display.rs index 878001f5..ab655681 100644 --- a/src/tree/display.rs +++ b/src/tree/display.rs @@ -34,6 +34,7 @@ impl DisplayNode { pub fn clear(&self) { self.outputs.clear(); + self.seat_state.clear(); } pub fn update_extents(&self) { diff --git a/src/tree/output.rs b/src/tree/output.rs index 9014826c..36c3cdf8 100644 --- a/src/tree/output.rs +++ b/src/tree/output.rs @@ -57,6 +57,7 @@ impl OutputNode { for workspace in workspaces { workspace.clear(); } + self.render_data.borrow_mut().titles.clear(); } pub fn on_spaces_changed(self: &Rc) { diff --git a/src/wheel.rs b/src/wheel.rs index e1602792..7735a6f9 100644 --- a/src/wheel.rs +++ b/src/wheel.rs @@ -132,6 +132,10 @@ impl Wheel { Ok(Rc::new(Wheel { data })) } + pub fn clear(&self) { + self.data.kill(); + } + fn future(&self) -> WheelTimeoutFuture { let data = self.data.cached_futures.pop().unwrap_or_else(|| { Rc::new(WheelTimeoutData {