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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Unreleased

- On Mac, `NSWindow` and supporting objects might be alive long after they were `closed` which resulted in apps consuming more heap then needed. Mainly it was affecting multi window applications. Not expecting any user visible change of behaviour after the fix.
- Fix regression of Window platform extensions for macOS where `NSFullSizeContentViewWindowMask` was not being correctly applied to `.fullsize_content_view`.
- Corrected `get_position` on Windows to be relative to the screen rather than to the taskbar.
- Corrected `Moved` event on Windows to use position values equivalent to those returned by `get_position`. It previously supplied client area positions instead of window positions, and would additionally interpret negative values as being very large (around `u16::MAX`).
Expand Down
2 changes: 1 addition & 1 deletion src/platform/macos/events_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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

});
}
}
Expand Down
93 changes: 77 additions & 16 deletions src/platform/macos/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use cocoa;
use cocoa::appkit::{self, NSApplication, NSColor, NSScreen, NSView, NSWindow, NSWindowButton,
NSWindowStyleMask};
use cocoa::base::{id, nil};
use cocoa::foundation::{NSDictionary, NSPoint, NSRect, NSSize, NSString};
use cocoa::foundation::{NSDictionary, NSPoint, NSRect, NSSize, NSString, NSAutoreleasePool};

use core_graphics::display::CGDisplay;

Expand Down Expand Up @@ -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(nil);

(&mut **delegate).set_ivar("winitState", state_ptr as *mut ::std::os::raw::c_void);
let _: () = msg_send![*state.window, setDelegate:*delegate];

let _: () = msg_send![autoreleasepool, drain];

WindowDelegate { state: state, _this: delegate }
}
}
Expand All @@ -464,7 +470,11 @@ impl Drop for WindowDelegate {
fn drop(&mut self) {
unsafe {
// Nil the window's delegate so it doesn't still reference us
// NOTE: setDelegate:nil at first retains the previous value,
// and then autoreleases it, so autorelease pool is needed
let autoreleasepool = NSAutoreleasePool::new(nil);
let _: () = msg_send![*self.state.window, setDelegate:nil];
let _: () = msg_send![autoreleasepool, drain];
}
}
}
Expand Down Expand Up @@ -505,13 +515,32 @@ unsafe fn get_current_monitor() -> RootMonitorId {

impl Drop for Window2 {
fn drop(&mut self) {
// Remove this window from the `EventLoop`s list of windows.
// The destructor order is:
// Window ->
// Rc<Window2> (makes Weak<..> in shared.windows None) ->
// Window2
// needed to remove the element from array
let id = self.id();
if let Some(shared) = self.delegate.state.shared.upgrade() {
shared.find_and_remove_window(id);
}

// nswindow::close uses autorelease
// so autorelease pool
let autoreleasepool = unsafe {
NSAutoreleasePool::new(nil)
};

// Close the window if it has not yet been closed.
let nswindow = *self.window;
if nswindow != nil {
unsafe {
let () = msg_send![nswindow, close];
}
}

let _: () = unsafe { msg_send![autoreleasepool, drain] };
}
}

Expand All @@ -538,20 +567,32 @@ impl Window2 {
panic!("Windows can only be created on the main thread on macOS");
}
}
let autoreleasepool = unsafe {
NSAutoreleasePool::new(nil)
};

let app = match Window2::create_app(pl_attribs.activation_policy) {
Some(app) => app,
None => { return Err(OsError(format!("Couldn't create NSApplication"))); },
None => {
let _: () = unsafe { msg_send![autoreleasepool, drain] };
return Err(OsError(format!("Couldn't create NSApplication")));
},
};

let window = match Window2::create_window(win_attribs, pl_attribs)
{
Some(window) => window,
None => { return Err(OsError(format!("Couldn't create NSWindow"))); },
None => {
let _: () = unsafe { msg_send![autoreleasepool, drain] };
return Err(OsError(format!("Couldn't create NSWindow")));
},
};
let view = match Window2::create_view(*window) {
Some(view) => view,
None => { return Err(OsError(format!("Couldn't create NSView"))); },
None => {
let _: () = unsafe { msg_send![autoreleasepool, drain] };
return Err(OsError(format!("Couldn't create NSView")));
},
};

unsafe {
Expand Down Expand Up @@ -618,6 +659,8 @@ impl Window2 {
window.delegate.state.perform_maximized(win_attribs.maximized);
}

let _: () = unsafe { msg_send![autoreleasepool, drain] };

Ok(window)
}

Expand All @@ -638,11 +681,29 @@ impl Window2 {
}
}

fn class() -> *const Class {
static mut WINDOW2_CLASS: *const Class = 0 as *const Class;
static INIT: std::sync::Once = std::sync::ONCE_INIT;

INIT.call_once(|| unsafe {
let window_superclass = Class::get("NSWindow").unwrap();
let mut decl = ClassDecl::new("WinitWindow", window_superclass).unwrap();
decl.add_method(sel!(canBecomeMainWindow), yes as extern fn(&Object, Sel) -> BOOL);
decl.add_method(sel!(canBecomeKeyWindow), yes as extern fn(&Object, Sel) -> BOOL);
WINDOW2_CLASS = decl.register();
});

unsafe {
WINDOW2_CLASS
}
}

fn create_window(
attrs: &WindowAttributes,
pl_attrs: &PlatformSpecificWindowBuilderAttributes)
-> Option<IdRef> {
unsafe {
let autoreleasepool = NSAutoreleasePool::new(nil);
let screen = match attrs.fullscreen {
Some(ref monitor_id) => {
let monitor_screen = monitor_id.inner.get_nsscreen();
Expand Down Expand Up @@ -685,14 +746,7 @@ impl Window2 {
NSWindowStyleMask::NSTitledWindowMask
};

let winit_window = Class::get("WinitWindow").unwrap_or_else(|| {
let window_superclass = Class::get("NSWindow").unwrap();
let mut decl = ClassDecl::new("WinitWindow", window_superclass).unwrap();
decl.add_method(sel!(canBecomeMainWindow), yes as extern fn(&Object, Sel) -> BOOL);
decl.add_method(sel!(canBecomeKeyWindow), yes as extern fn(&Object, Sel) -> BOOL);
decl.register();
Class::get("WinitWindow").unwrap()
});
let winit_window = Window2::class();

let window: id = msg_send![winit_window, alloc];

Expand All @@ -702,7 +756,7 @@ impl Window2 {
appkit::NSBackingStoreBuffered,
NO,
));
window.non_nil().map(|window| {
let res = window.non_nil().map(|window| {
let title = IdRef::new(NSString::alloc(nil).init_str(&attrs.title));
window.setReleasedWhenClosed_(NO);
window.setTitle_(*title);
Expand Down Expand Up @@ -735,7 +789,9 @@ impl Window2 {

window.center();
window
})
});
let _: () = msg_send![autoreleasepool, drain];
res
}
}

Expand Down Expand Up @@ -1033,7 +1089,7 @@ impl Window2 {

// Convert the `cocoa::base::id` associated with a window to a usize to use as a unique identifier
// for the window.
pub fn get_window_id(window_cocoa_id: cocoa::base::id) -> Id {
pub fn get_window_id(window_cocoa_id: id) -> Id {
Id(window_cocoa_id as *const objc::runtime::Object as usize)
}

Expand Down Expand Up @@ -1109,7 +1165,12 @@ impl IdRef {
impl Drop for IdRef {
fn drop(&mut self) {
if self.0 != nil {
let _: () = unsafe { msg_send![self.0, release] };
unsafe {
let autoreleasepool =
NSAutoreleasePool::new(nil);
let _ : () = msg_send![self.0, release];
let _ : () = msg_send![autoreleasepool, release];
};
}
}
}
Expand Down