-
Notifications
You must be signed in to change notification settings - Fork 945
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
Leaking DeviceContext instances in unit tests #3450
Comments
/cc @JeremyKuhne these are probably interesting to look at whenever you get around to turning |
@weltkante The hope is that we can keep almost everything on the stack. In any cases where we're forced to maintain a longer lifetime I'll look for a separate and more targeted tracking mechanism. |
We should fix this right? (If we can) |
(slightly offtopic, this comment is not about the leaks in the issue description, but on my side comment about a wrong dispose pattern; mentioning it explicitly to avoid confusion) If we are going to replace DeviceContext by a struct anyways its not worth the effort to correct this, in particular since changing the code to conform to the dispose pattern may introduce new edge cases. Right now it happens to work out correctly even if things go wrong, several mistakes end up aligning just right so that the indeterministic finalizer order doesn't make a difference in this scenario, the only net effect is an unnecessary finalizer call. Fixing the dispose pattern to pass the correct flag and be independent of finalizer order would require very careful review not to introduce new leaks. |
#3553 should take care of this |
.NET Core Version:
github master
Have you experienced this same bug with .NET Framework?:
unknown (cannot debug Desktop Framework source to determine if ErrorProvider leaks there as well)
Problem description:
While investigating control handle leaks in the unit tests I also noticed significant
DeviceContext
leaks. They are not permanent leaks, the GC does clean them up, but they are nondeterministic and released on a different thread than they were created on - don't know if thats any issue, probably not since tests seem to be running fine generally. I also don't know if there is any relation to previously observed GDI resource bottlenecks on the CI machine.WindowsGraphicsCacheManager
caches aDeviceContext
for each UI thread but never releases it, so it will leak and be GC'ed. Since unit testing creates a lot of test threads this also cycles a lot ofDeviceContext
instances through the GC.Stack Trace Example
Test:
System.Windows.Forms.Tests.AccessibleObjects.MonthCalendarAccessibleObjectTests.MonthCalendarAccessibleObject_GetCalendarCell_DoesntThrowException_If_ParentAccessibleObject_IsNull
Stacktrace:
The
ErrorProvider
creates a private nested classErrorWindow
which leaks multipleDeviceContext
instances fromCreateMirrorDC
which are never disposed correctly even though theErrorProvider
and its controls seem to be disposed properly.This happens in the
ErrorProviderAccessibleObjectTests
which now does haveDispose
method and is verified to be called, but also on otherErrorProvider
tests (see example C) which don't use instance-level fields. I may have overlooked something but it is not clear to me what additionalDispose
calls are needed to clean up anErrorProvider
correctly, so its currently unknown if its tests not disposing properly or the implementation not disposing properly.Stack Trace Example A
Test:
System.Windows.Forms.Tests.AccessibleObjects.ErrorProviderAccessibleObjectTests.ErrorProvider_NameDoesntEqualControlTypeOrChildName
Stacktrace:
Stack Trace Example B
Test:
System.Windows.Forms.Tests.AccessibleObjects.ErrorProviderAccessibleObjectTests.ErrorProvider_CorrectErrorValue
Stacktrace:
Stack Trace Example C
Test:
System.Windows.Forms.Tests.ErrorProviderTests.ErrorProvider_Items_CallEvents_Success
Stacktrace:
Expected behavior:
Ideally deterministic cleanup of GDI resources (instead of letting the GC clean up lazily)
Minimal repro:
Run unit tests under a debugger and put a conditional breakpoint in
DeviceContext
finalizer. Condition it on_disposed == false
.(Sometimes the finalizer is called on disposed objects, thats an issue with finalization order and cross-class internal calls from other finalized classes to
Dispose(bool)
not implementing the disposable pattern correctly. Doesn't cause any harm just a redundant call.)The text was updated successfully, but these errors were encountered: