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

CBLListener retaining CBL_RunLoopServer so both never get freed. #1921

Closed
brendand opened this issue Oct 6, 2017 · 10 comments
Closed

CBLListener retaining CBL_RunLoopServer so both never get freed. #1921

brendand opened this issue Oct 6, 2017 · 10 comments
Assignees
Milestone

Comments

@brendand
Copy link
Contributor

brendand commented Oct 6, 2017

I have a TFDatabaseDocument class that holds on to a CBLListener and a CBLManager instance.

I create the CBLListener like this:

self.syncListener = [[CBLListener alloc] initWithManager:self.couchManager port:0];
self.syncListener.delegate = self;

When I close my TFDatabaseDocument class, I do these things in a stop method I call before the document is closed:

[self.syncListener stop];
self.syncListener.delegate = nil;
self.syncListener = nil;

I also stop the CBLManager and nil it out too, just to be sure.

I can confirm that dealloc is definitely being called when my TFDatabaseDocument class closes and it's deallocated properly.

However, when I use the memory graph debugger in Xcode, I see that the CouchbaseLite CBL_RunLoopServer and a bunch of classes in CouchbaseLiteListener are still in memory.

Here's what the memory graph debugger shows in Xcode 9:

screen shot 2017-10-05 at 7 00 35 pm

screen shot 2017-10-05 at 7 00 45 pm

I tested to see what happens when I don't instantiate a CBLListener and I find that CouchbaseLite CBL_RunLoopServer no longer appears in the Xcode 9 memory debugger when I release my class.

So it seems to me that when you instantiate a listener, it's retaining the CBL_RunLoopServer even when you deallocate your class that holds the listener. And this prevents the listener from freeing up all of its resources and the CBL_RunLoopServer.

I've tested this both on macOS 10.13 and iOS 11. Same results on both.

Is there another way I should be trying to clear out the CBLListener?


  • Version: 1.4.1
  • Client OS: macOS 10.13 High Sierra and iOS 11.
  • Server: n/a
@pasin pasin self-assigned this Oct 6, 2017
@pasin
Copy link
Contributor

pasin commented Oct 6, 2017

From the graph, I could see the circular references between CBLHTTPListener and CBLHTTPServer. Probably one of them needs to be weak reference.

@pasin pasin added this to the 1.4.1 milestone Oct 6, 2017
@brendand
Copy link
Contributor Author

brendand commented Oct 6, 2017

And I just found out the purple [!] indicate leaks.

@brendand
Copy link
Contributor Author

brendand commented Oct 6, 2017

As a test I set the listener on CBLHTTPConnection.h to weak:

@property (weak) CBLListener* listener;

That improved things so now the CBL_RunLoopServer no longer exists after closing. But a couple of classes in the CouchbaseLiteListener are still hanging around. Namely CBL_DDLoggerNode and CBL_MYDDLogger.

That was just a test though to see what would happen. I'm sure you'll have a better fix.

@brendand
Copy link
Contributor Author

brendand commented Oct 8, 2017

There might be other places that it's not letting go.

So even though I deallocate the place where I originally created the CBLManager, I get these leaks in the CouchbaseLite framework here after my object is gone.

screen shot 2017-10-08 at 4 17 38 pm

@brendand
Copy link
Contributor Author

brendand commented Oct 8, 2017

If I don't startup the listener, then the CouchbaseLite framework classes properly get deallocated when I close my file.

@brendand
Copy link
Contributor Author

brendand commented Oct 8, 2017

Adding this code to remove the loggers in CBLHTTPListener helps to free up the listener too:

- (void)dealloc {
	[DDLog removeAllLoggers];
}

@brendand
Copy link
Contributor Author

brendand commented Oct 9, 2017

Perhaps going a bit ballistic to try and resolve this issue, I've been doing more experimentation with cleaning up things:

CBLSyncListener.m

Changed CBLManager *_manager to:

__weak CBLManager* _manager;

added these to dealloc:

	[_netService stop];
	_netService.delegate = nil;
	_manager = nil;
	_facade = nil;
	_queue = nil;
	[_handlers removeAllObjects];

CBLManager.m

added to close method:

[_replications removeAllObjects];

CBLHTTPConnection.h

changed:

@property (retain) CBLListener* listener;
@property (retain) CBL_Server* cblServer;

to:

@property (weak) CBLListener* listener;
@property (weak) CBL_Server* cblServer;

So maybe overkill in some spots. But it seems to get the job done.

@pasin
Copy link
Contributor

pasin commented Oct 9, 2017

  • (void)dealloc {
    [DDLog removeAllLoggers];
    }

This might not be correct for example if starting 2 replicators. Also if you don't enable Listener logging, you will not see the DD_Logger objects - I guessed your production release will not have the Listener logging enabled.

@pasin
Copy link
Contributor

pasin commented Oct 9, 2017

Somehow I don't see the replicator leaks as you mentioned in #1921 (comment). Also it is a good idea to create a separate ticket for replicator leak.

pasin added a commit that referenced this issue Oct 9, 2017
From #1921, the leak is caused by the circular references between CBLHTTPConnection and CBLHTTPListener. Making the listener property in CBLHTTPServer as a weak reference should fix the issue.
@pasin
Copy link
Contributor

pasin commented Oct 9, 2017

@brendand I did a minimum fix. I don't fix the Logger issue as the CBL_MYDDLogger is created once for the CBLHTTPListener class, and it will be created only when the Listener logging is enabled. For the Replicator leaks issue, please create a new ticket.

@pasin pasin closed this as completed Oct 9, 2017
@pasin pasin removed the ready label Oct 9, 2017
@djpongh djpongh added the bug label Nov 13, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants