-
Notifications
You must be signed in to change notification settings - Fork 1
Unsubscribe the ProcessExit handler on dispose #312
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -9,6 +9,7 @@ namespace ktsu.AppDataStorage.Test; | |||||||||
| using System.IO.Abstractions; | ||||||||||
| using System.IO.Abstractions.TestingHelpers; | ||||||||||
| using System.Net.Sockets; | ||||||||||
| using System.Runtime.CompilerServices; | ||||||||||
| using System.Text.Json; | ||||||||||
|
|
||||||||||
| using ktsu.CaseConverter; | ||||||||||
|
|
@@ -474,6 +475,35 @@ public void TestDisposeCanBeCalledMultipleTimes() | |||||||||
| Assert.IsTrue(appData.IsDisposeRegistered, "IsDisposeRegistered should remain true after multiple disposals."); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| // Kept out of the test body so the instance cannot survive in a local slot the JIT has not yet | ||||||||||
| // reported dead, which would make the weak reference stay alive for reasons unrelated to the | ||||||||||
| // event handler. | ||||||||||
| [MethodImpl(MethodImplOptions.NoInlining)] | ||||||||||
| private static WeakReference RegisterQueueSaveAndDispose() | ||||||||||
| { | ||||||||||
| TestAppData appData = new(); | ||||||||||
| appData.QueueSave(); | ||||||||||
| Assert.IsTrue(appData.IsDisposeRegistered, "QueueSave should have registered the process-exit handler."); | ||||||||||
| appData.Dispose(); | ||||||||||
| return new WeakReference(appData); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| [TestMethod] | ||||||||||
| public void TestDisposeReleasesTheProcessExitHandlerSoTheInstanceCanBeCollected() | ||||||||||
| { | ||||||||||
| // The handler closes over the instance and AppDomain.CurrentDomain lives for the whole | ||||||||||
| // process, so failing to unsubscribe roots every instance that ever queued a save. That is | ||||||||||
| // invisible for a single Get() singleton but unbounded for a process that churns instances | ||||||||||
| // through LoadOrCreate. | ||||||||||
| WeakReference reference = RegisterQueueSaveAndDispose(); | ||||||||||
|
|
||||||||||
| GC.Collect(); | ||||||||||
| GC.WaitForPendingFinalizers(); | ||||||||||
| GC.Collect(); | ||||||||||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Verified, and the call sequence stays. The rule is right about the pattern and wrong about this use, and the suggested replacement would remove what the test measures. The alert is a true positive on the pattern. There is no alternative API. The test asserts that a disposed The suggested fix does not work, and the comment says so itself. It offers three alternatives and disclaims each one in the same breath — The three calls are each load-bearing, which is why there are three rather than one:
Dropping the second On suppressing it instead: Worth noting the gating check agrees: CodeQL itself reported Generated by Claude Code |
||||||||||
|
|
||||||||||
| Assert.IsFalse(reference.IsAlive, "A disposed instance should not still be rooted by the ProcessExit handler."); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| [TestMethod] | ||||||||||
| public void TestGetReturnsSameInstance() | ||||||||||
| { | ||||||||||
|
|
||||||||||
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.
Checked this one, and it stays as written — reasoning on the companion alert at line 502, which flags the second
GC.Collect()in the same three-call sequence.Generated by Claude Code