Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Macos multi windows leak #481

Merged
merged 8 commits into from
Apr 28, 2018
Merged

Conversation

bogdad
Copy link
Contributor

@bogdad bogdad commented Apr 22, 2018

made multi window leak less memory

created a simple test which spawns windows and recreates them on "return" key press, the title of the window is the number of windows created.

before the change
a lot of objects created

after the change:
not a lot of objects created

and more significant:

total persisted before:

a lot persisted

and total persisted after

not a lot persisted

The gist of the change is the adding NSAutorelease pools, as in most places where we are calling objc it uses NSObject:autorelease which puts the object in the top most autorelease pool on the stack, or it is never released if no such autorelease pool exist,
or if the topmost autorelease pool is outside of the event loop, i.e. gets freed after the thread terminates.

This is continuation of #303 which was an incorrect attempt to do the same thing.
This probably fixes #158

I know its hard to check that without instruments and looking at the lists of allocations,
not sure how to best approach approving this pr

@edwin0cheng
Copy link
Contributor

Just a little reminder, same as #428:

Due to the recent changes in the Rust compiler, unconstrained type
variables are now deduced to ! instead of (). There are some
occurrences where msg_send! is used without constraining its return
type (relying on the assumption that they would be deduced to be ()).
As a result, the macOS port of winit stopped working in nightly.

So all msg_send! call should be set a return value expliclty, just like this:

let _ : () = msg_send![...];

@bogdad
Copy link
Contributor Author

bogdad commented Apr 24, 2018

@edwin0cheng hi! thank you, fixed!

@francesca64
Copy link
Member

Alright, I'm back on macOS, so I can review this now. First of all, those statistics look great!

I can still reproduce #158 on this branch, but after rebasing it seems completely fixed. Be sure to double-check that.

Before this can merge:

  • examples/recreate_window_leak.rs needs to be removed, since it's not useful for users. You can embed it in your PR description or as a gist if you still want it to be accessible.
  • This should have a CHANGELOG entry. I know it's not straightforward to write one for something abstract like this, but a good overview of what this improves will both A) make the next winit release seem cooler, and B) give people an idea of what things to check if they experience regressions.

Note that at the time of writing, I haven't actually finished reviewing this yet, so there could be other things. I just wanted to let you know this was being worked on.

@bogdad
Copy link
Contributor Author

bogdad commented Apr 28, 2018 via email

bogdad added 6 commits April 28, 2018 13:11
as setDelegate:nil autoreleases WindowDelegate during work.

Added NSAutoReleasepool for Window2::Create,
as it uses autorelease on objects while doing work.

Added NSAutoreleasepool for Window2::Drop
as nswindow::close uses autorelease on objects.

Added NSAutoreleasepool for IdRef.

Moved Window2 WinitWindow objc class to a static var, as we are creating
multiple windows.
@bogdad bogdad force-pushed the macos_window_leak branch from c657566 to 69096b6 Compare April 28, 2018 12:06
@bogdad
Copy link
Contributor Author

bogdad commented Apr 28, 2018

Rebased, added change log entry (not really sure it is a good one, please advise), checked the multiwindow example,

this shows amount of alive window objects

screenshot 2018-04-28 14 10 59

screenshot 2018-04-28 14 11 08

screenshot 2018-04-28 14 11 17

seems they are released as I close them

this is how it looks like on a master:
screenshot 2018-04-28 14 21 08

tried to repro 158 with multi desktops but could not.

There is still some bytes leaked, namely some NSStrings and titles, but they are neglible in total memory usage, lets deal with them in the future.

Can also confirm that this branch (without rebasing) alone did not fix 158 issue, which is interesting,
trying to figure out why meanwhile. (this is the branch with the previous state on which multiwindow example does not release windows in time https://github.com/bogdad/winit/tree/wo_rebasing)

@@ -94,7 +94,7 @@ impl Shared {
if let Ok(mut windows) = self.windows.lock() {
windows.retain(|w| match w.upgrade() {
Some(w) => w.id() != id,
None => true,
None => false,
Copy link
Contributor Author

@bogdad bogdad Apr 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dont need to keep dead week ptrs here

@@ -452,9 +452,15 @@ impl WindowDelegate {
unsafe {
let delegate = IdRef::new(msg_send![WindowDelegate::class(), new]);

// setDelegate uses autorelease on objects,
// so need autorelease
let autoreleasepool = NSAutoreleasePool::new(cocoa::base::nil);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason you went with cocoa::base::nil instead of using the imported nil? It's a minor gripe, but having that consistent makes it easier for people to tell the same type is being used throughout.

@francesca64
Copy link
Member

Rebased, added change log entry (not really sure it is a good one, please advise)

Thanks. The CHANGELOG entry looks good.

checked the multiwindow example,

Thanks for doing that. It gives me good peace of mind.

Can also confirm that this branch (without rebasing) alone did not fix 158 issue, which is interesting, trying to figure out why meanwhile.

Prior to #476, windows in the multiwindow example weren't dropped until the program exited. Prior to #481, the drop implementation wasn't releasing windows correctly. With both, windows drop at the correct time and are dropped the right way.

@@ -638,11 +679,30 @@ impl Window2 {
}
}

fn class() -> *const Class {

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra newline

shared.find_and_remove_window(id);
}


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra newline

None => { return Err(OsError(format!("Couldn't create NSApplication"))); },
None => {
let _: () = unsafe { msg_send![autoreleasepool, drain] };
return Err(OsError(format!("Couldn't create NSApplication"))); },
Copy link
Member

@francesca64 francesca64 Apr 28, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The }, should be on a newline. (This applies to the 2 matches below as well)

@francesca64
Copy link
Member

In terms of functionality, everything looks good. I pretty much just gave you some formatting nitpicks (sorry about those, but I'm hoping to get winit to look more cohesive internally).

// Remove this window from the `EventLoop`s list of windows.
// The destructor order is:
// Window ->
// Rc<Window2> (makes Weak<..> in shared.windows none) ->
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would also be nice to have none be None, for clarity. We should be good to merge after this.

@bogdad
Copy link
Contributor Author

bogdad commented Apr 28, 2018

Fixed all code formatting errors, sorry about that, and thank you for all the work and explanation!

@francesca64
Copy link
Member

Thanks!

@francesca64 francesca64 merged commit 3407a8d into rust-windowing:master Apr 28, 2018
@bogdad bogdad deleted the macos_window_leak branch June 2, 2018 13:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

[macOS] NSWindows are not released when closed, causing odd zombie window behaviour in some cases.
3 participants