Fix false-positives when using ResourceLeakDetector.#6087
Conversation
| // | ||
| // See also https://github.com/netty/netty/issues/6034 | ||
| // | ||
| return leak.close() && trackedObject != null; |
There was a problem hiding this comment.
I wonder if this may be optimized away at some point...
There was a problem hiding this comment.
I think it can't as we use it in the return value... if I remove it the false positives start again, with it not
There was a problem hiding this comment.
@normanmaurer - I wonder if we should consider this an API bug and just change the ResourceLeak API ... if others are using this interface they may be susceptible to the same bug without knowing ... changing the API will force them to fix the bug.
There was a problem hiding this comment.
@Scottmitch I was considering this but decided against this as this may break code when people upgrade. False-positives are bad but not as bad as breaking code.
That said I wonder if we maybe could just extend ResourceLeak and add the method there and then do an instanceof and if so call the new method. WDYT ? We would still deprecate the old method tho.
There was a problem hiding this comment.
Even better we could also add another method to ResourceLeakDetector which returns this new subclass and use it in our code. Then we mark ResourceLeakDetector.open(...) as deprecated.
@Scottmitch let me do this and add as a separate commit to this pr so you can review more easily
There was a problem hiding this comment.
@normanmaurer - the new ResourceLeakTracker lgtm ... can we just delete this utility method and add the warning on the close method in ResourceLeak? Just less code to maintain.
There was a problem hiding this comment.
can we just return derived in this case? What benefit does wrapping in a "leak aware buffer" with NoopResourceLeak provide? Or should we add a package private method on ResourceLeakDetector so that we can force a DefaultResourceLeak to be allocated (only used in this situation when the buffer is derived from another buffer which already has leak info)?
There was a problem hiding this comment.
@Scottmitch it was only basically to be consistent and always return the wrapper. That said I think it would also be fine to just return derived. Let me change this
There was a problem hiding this comment.
are there other types of buffers that we should do buf.unwrap() to? Also is it possible that the SwappedByteBuf will not be the "top level" and we need to go through the "unwrap" chain to properly find/unwrap it?
There was a problem hiding this comment.
Let me think a bit more about this but I think no...
There was a problem hiding this comment.
what is its here? can you link to an issue or provide more context?
There was a problem hiding this comment.
should we catch Throwable after the other exceptions (e.g. InterruptedException) to make sure we bubble up the exception and it is visible from the junit thread?
10c5e66 to
8927bfa
Compare
There was a problem hiding this comment.
@Scottmitch I need to adjust the comment here if you agree how I fixed it now makes sense in terms of API changes.
6cfe500 to
e016579
Compare
There was a problem hiding this comment.
I wonder if ResourceLeakRecoder makes more sense since the action this interface provides is record ... or maybe change the methods to track?
There was a problem hiding this comment.
I kind of like ResourceLeakTracker more... @nmittler any preferences ?
There was a problem hiding this comment.
Maybe consider rewording or adding the following:
After this method is called a leak associated with this ResourceLeakTracker should not be reported.
There was a problem hiding this comment.
Add a warning as to this API (specifically close) may result in false positives.
There was a problem hiding this comment.
also add a todo here to make private again once #6081 is fixed.
There was a problem hiding this comment.
rename to NoopByteBufResourceLeakTracker or just make this class generic (preferably the latter)
| // | ||
| // See also https://github.com/netty/netty/issues/6034 | ||
| // | ||
| return leak.close() && trackedObject != null; |
There was a problem hiding this comment.
@normanmaurer - the new ResourceLeakTracker lgtm ... can we just delete this utility method and add the warning on the close method in ResourceLeak? Just less code to maintain.
Scottmitch
left a comment
There was a problem hiding this comment.
ResourceLeakTracker lgtm .. few more comments though
e016579 to
32647a6
Compare
| shutdown(); | ||
| if (leak != null) { | ||
| leak.close(); | ||
| boolean closed = leak.close(ReferenceCountedOpenSslEngine.this); |
|
|
||
| if (leak != null) { | ||
| leak.close(); | ||
| boolean closed = leak.close(this); |
|
|
||
| if (leak != null) { | ||
| leak.close(); | ||
| boolean closed = leak.close(this); |
| final ResourceLeakTracker<DnsMessage> leak = this.leak; | ||
| if (leak != null) { | ||
| leak.close(); | ||
| boolean closed = leak.close(this); |
| destroy(); | ||
| if (leak != null) { | ||
| leak.close(); | ||
| boolean closed = leak.close(ReferenceCountedOpenSslContext.this); |
21b5778 to
85e3420
Compare
|
@Scottmitch PTAL again... I also added a few more asserts and shares some more code. I am quite happy now with the result :) |
| // collection before we actually get a chance to close the enclosing ResourceLeak. | ||
| boolean closed = close() && trackedObject != null; | ||
|
|
||
| // Ensure that the object that was tracked is the same as the one that was passed to close(...). |
There was a problem hiding this comment.
@Scottmitch note this check... This also ensure we really pass in the correct object and so the GC can not collect it before we called close() :)
There was a problem hiding this comment.
Asserts could be eliminated by JIT compiler (especially, when disabled), so I don't think relying on assert here would buy you much.
There was a problem hiding this comment.
@vlsi thi was not done to prevent GC or such, just to ensure we do the right thing. That said maybe this should be guarded by an IllegalArgumentException. @Scottmitch WDYT ?
There was a problem hiding this comment.
@normanmaurer , I see. Would you probably add an explanation message then to the assert statement?
There was a problem hiding this comment.
I'm fine with it being an assert for now as this is typically an internal Netty concern and thus should be caught by unit tests, and it not required to be enforced by the interface. If it becomes an issue we can always make the check more strict.
Can we move the assert to the top of the method and just directly return close() && trackedObject != null after?
| // Store the hash of the tracked object to later assert it in the close(...) method. | ||
| // It's important that we not store a reference to the referent as this would disallow it from | ||
| // be collected via the PhantomReference. | ||
| trackedHash = System.identityHashCode(referent); |
| private final ResourceLeak leak; | ||
| // This is always the original ByteBuf whichs reference-count is responsible to signal when the ResourceLeakTracker | ||
| // should be closed. This means this always must be the ByteBuf that is used when calling LeakDetector.track(...). | ||
| private final ByteBuf trackedByteBuf; |
There was a problem hiding this comment.
@Scottmitch see the ResourceLeakDetector.close(...) method why I now keep track of this one as well
| private void closeLeak(ByteBuf trackedByteBuf) { | ||
| // Close the ResourceLeakTracker with the tracked ByteBuf as argument. This must be the same that was used when | ||
| // calling DefaultResourceLeak.track(...). | ||
| boolean closed = leak.close(trackedByteBuf); |
| private void closeLeak() { | ||
| // Close the ResourceLeakTracker with the tracked ByteBuf as argument. This must be the same that was used when | ||
| // calling DefaultResourceLeak.track(...). | ||
| boolean closed = leak.close(trackedByteBuf); |
|
|
||
| // Called from within SimpleLeakAwareByteBuf and AdvancedLeakAwareByteBuf. | ||
| final void parent(ByteBuf newParent) { | ||
| assert newParent instanceof SimpleLeakAwareByteBuf || newParent instanceof AdvancedLeakAwareByteBuf; |
There was a problem hiding this comment.
@Scottmitch I could even change this to assert newParent instanceof SimpleLeakAwareByteBuf as AdvancedLeakAwareByteBufnow extends SimpleLeakAwareByteBuf . Not sure if its better to be explicit here tho. WDYT ?
There was a problem hiding this comment.
just checking SimpleLeakAwareByteBuf sgtm ... its an assert meant for debugging ... if the class hierarchy changes the assert will trip and we can update it.
There was a problem hiding this comment.
true enough... will change :)
|
@Scottmitch rest is good ? |
| class SimpleLeakAwareByteBuf extends WrappedByteBuf { | ||
|
|
||
| private final ResourceLeak leak; | ||
| // This is always the original ByteBuf whichs reference-count is responsible to signal when the ResourceLeakTracker |
There was a problem hiding this comment.
/**
* This object's is associated with the {@link ResourceLeakTracker}.
* When {@link ResourceLeakTracker#close(Object}} is called this object will be
* used as the argument. It is also assumed that this object is used when
* {@link ResourceLeakDetector#track(Object)} is called to create {@link #leak}.
*/| // collection before we actually get a chance to close the enclosing ResourceLeak. | ||
| boolean closed = close() && trackedObject != null; | ||
|
|
||
| // Ensure that the object that was tracked is the same as the one that was passed to close(...). |
There was a problem hiding this comment.
I'm fine with it being an assert for now as this is typically an internal Netty concern and thus should be caught by unit tests, and it not required to be enforced by the interface. If it becomes an issue we can always make the check more strict.
Can we move the assert to the top of the method and just directly return close() && trackedObject != null after?
Motivation: We need to ensure the tracked object can not be GC'ed before ResourceLeak.close() is called as otherwise we may get false-positives reported by the ResourceLeakDetector. This can happen as the JIT / GC may be able to figure out that we do not need the tracked object anymore and so already enqueue it for collection before we actually get a chance to close the enclosing ResourceLeak. Modifications: - Add ResourceLeakTracker and deprecate the old ResourceLeak - Fix some javadocs to correctly release buffers. - Add a unit test for ResourceLeakDetector that shows that ResourceLeakTracker has not the problems. Result: No more false-positives reported by ResourceLeakDetector when ResourceLeakDetector.track(...) is used.
85e3420 to
fed6acd
Compare
|
@Scottmitch all addressed ... PTAL again |
Motivation: While working on #6087 some buffer leaks showed up. Modifications: Correctly release buffers. Result: No more buffer leaks in memcache and stomp codec tests.
Motivation: While working on #6087 some buffer leaks showed up. Modifications: Correctly release buffers. Result: No more buffer leaks in memcache and stomp codec tests.
|
@normanmaurer - Good work ... ship it! |
|
Cherry-picked into 4.1 (c2f4daa) . Will open an extra pr for 4.0 as there is a bit more work to do. |
Motivation: While working on netty#6087 some buffer leaks showed up. Modifications: Correctly release buffers. Result: No more buffer leaks in memcache and stomp codec tests.


Motivation:
We need to ensure the tracked object can not be GC'ed before ResourceLeak.close() is called as otherwise we may get false-positives reported by the ResourceLeakDetector. This can happen as the JIT / GC may be able to figure out that we do not need the tracked object anymore and so already enqueue it for collection before we actually get a chance to close the enclosing ResourceLeak.
Modifications:
Result:
No more false-positives reported by ResourceLeakDetector when ResourceLeakDetector.track(...) is used.