all: fix memory leaks
This commit is contained in:
parent
56e3eee629
commit
e5c0916a25
15 changed files with 147 additions and 54 deletions
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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>),
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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> {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
64
src/leaks.rs
64
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::<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
|
||||
|
|
|
|||
22
src/state.rs
22
src/state.rs
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ impl DisplayNode {
|
|||
|
||||
pub fn clear(&self) {
|
||||
self.outputs.clear();
|
||||
self.seat_state.clear();
|
||||
}
|
||||
|
||||
pub fn update_extents(&self) {
|
||||
|
|
|
|||
|
|
@ -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>) {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue