1
0
Fork 0
forked from wry/wry

all: fix memory leaks

This commit is contained in:
Julian Orth 2022-05-18 22:42:36 +02:00
parent 56e3eee629
commit e5c0916a25
15 changed files with 147 additions and 54 deletions

View file

@ -158,10 +158,21 @@ impl<T, F: Future<Output = T>> Task<T, F> {
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);

View file

@ -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<Option<TransformMatrix>>,
}
impl Drop for MetalInputDevice {
fn drop(&mut self) {
if let Some(fd) = self.fd.take() {
mem::forget(fd);
}
}
}
#[derive(Clone)]
enum MetalDevice {
Input(Rc<MetalInputDevice>),

View file

@ -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<ForkerProxy> {
@ -99,7 +102,7 @@ const STATIC_VARS: &[(&str, &str)] = &[
pub type TestFuture = Box<dyn Fn(&Rc<State>) -> Box<dyn Future<Output = ()>>>;
fn start_compositor2(
forker: Rc<ForkerProxy>,
forker: Option<Rc<ForkerProxy>>,
logger: Option<Arc<Logger>>,
run_args: RunArgs,
test_future: Option<TestFuture>,
@ -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<State>) {
connected: Cell::new(true),
name: "Dummy".to_string(),
drm_dev: None,
async_event: Default::default(),
}),
0,
&backend::Mode {

View file

@ -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<Self, ForkerError> {

View file

@ -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 {

View file

@ -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();
}
}

View file

@ -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);

View file

@ -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 {

View file

@ -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::<usize>(),
)
};
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

View file

@ -161,6 +161,7 @@ pub struct InputDeviceData {
pub handler: SpawnedFuture<()>,
pub id: InputDeviceId,
pub data: Rc<DeviceHandlerData>,
pub async_event: Rc<AsyncEvent>,
}
pub struct DeviceHandlerData {
@ -174,6 +175,7 @@ pub struct ConnectorData {
pub connected: Cell<bool>,
pub name: String,
pub drm_dev: Option<Rc<DrmDevData>>,
pub async_event: Rc<AsyncEvent>,
}
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();
}
}

View file

@ -27,6 +27,7 @@ pub fn handle(state: &Rc<State>, connector: &Rc<dyn Connector>) {
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<AsyncEvent>, 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() {

View file

@ -12,10 +12,12 @@ pub fn handle(state: &Rc<State>, dev: Rc<dyn InputDevice>) {
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<State>, dev: Rc<dyn InputDevice>) {
handler,
id: dev.id(),
data,
async_event: ae,
},
);
}
pub struct DeviceHandler {
pub state: Rc<State>,
pub dev: Rc<dyn InputDevice>,
pub data: Rc<DeviceHandlerData>,
struct DeviceHandler {
state: Rc<State>,
dev: Rc<dyn InputDevice>,
data: Rc<DeviceHandlerData>,
ae: Rc<AsyncEvent>,
}
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());

View file

@ -34,6 +34,7 @@ impl DisplayNode {
pub fn clear(&self) {
self.outputs.clear();
self.seat_state.clear();
}
pub fn update_extents(&self) {

View file

@ -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<Self>) {

View file

@ -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 {