-
Notifications
You must be signed in to change notification settings - Fork 19
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
stop leaking memory by avoiding retain cycle in callback referring to itself #63
Conversation
Fixes #62 for me. |
Codecov Report
@@ Coverage Diff @@
## master #63 +/- ##
==========================================
+ Coverage 65.18% 65.33% +0.15%
==========================================
Files 5 5
Lines 247 251 +4
==========================================
+ Hits 161 164 +3
- Misses 86 87 +1
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR and identifying this retain cycle.
As it stands, this fix doesn't work for concurrent requests, for two reasons:
- If the
callback
closures for two concurrent requests overlap in execution, the creation of the second will overwrite thecallback
property and we will crash (because the first closure will have anunowned
reference to a closure that has been released). - If two requests A and B both enter the same Credentials instance concurrently, and both assign
callback
at the same time, then I suspect we'll either crash, or invoke the same callback (eg. pointing to request B) twice, which would most likely also result in a crash, but could result in request A appearing to skip the middleware.
Both problems stem from the fact that callback
is now an instance property (one per instance) rather than a local variable (one per call to handle()
).
You can observe this problem by running your test_load.sh
multiple times concurrently (two instances was enough in my case).
I think we can actually make a slightly simpler fix: rather than extracting the callback as an instance property, leave it as-is, and set callback = nil
for every code path in the closure where we do not call ourself back.
It looks like we need to do this in four places: for the onSuccess
, onFailure
and inProgress
cases, and in the else
where all the plugins answered pass.
Thanks @djones6 for very valid comments. I did not consider concurrency at all, I wanted to make sure there are no retain cycles in the first place. I'm not familiar with the codebase that much - just landed in the code as it was leaking :) so let me spend some time on this to see if I can address it properly... no promises though to set the expectations correctly :) |
FWIW, this is the fix I tried locally when I wrote the above comment (I'm quite happy for you to use this in your PR if you like). It seems to fix the problem and works with concurrent requests. https://gist.github.com/djones6/a3b488812af82af64f6f679143e80ad9 |
…to itself" This reverts commit 471036a.
Thanks @djones6 for suggested fix, I have re-examined, reverted my changes, used your suggested fix, retested in both instruments and Xcode with multiple I have not verified all code paths thus, but I trust your knowledge of the code 👍 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Description
Mu Kitura based HTTPS server with basic auth has been leaking memory for the past months and I finally found a time to investigate. The sample project https://github.com/mman/leaky-kitura demonstrates this issue nicely.
Motivation and Context
The line https://github.com/IBM-Swift/Kitura-Credentials/blob/master/Sources/Credentials/Credentials.swift#L99 creates a retain cycle that from my investigation keeps all requests in memory eating the memory with every authenticated request passing through Credentials.handle method.
Tested using malloc debugger in Xcode 9.2 and Xcode 9.3 as well as in instruments. With this proposed change the process memory usage remains stable.
Checklist: