-
Notifications
You must be signed in to change notification settings - Fork 60
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
Finalizer Throws Unhandled Exception #246
Comments
Thanks for raising this, I guess its coming from here. Do you know what the error status is? Ill do some research on canonical ways to handle this. |
I think the Tag wasn't found on the device, hence the "ErrorNotFound" in the libplctag exception. Typically, we have a Dispose pattern in C# The dispose pattern has a few portions A public void Dispose(); method - this is the one implementing the IDisposable interface a private void Dispose(bool disposing) method - this one helps us check whether we're being disposed by the programmer deliberately, or by the finalizer The Finalizer should call Dispose(false); the IDisposable Dispose calls Dispose(true); Consider the following example, which is the dispose pattern:
|
I have spent a few weeks on and off trying to understand this. I was caught up on thinking that it was an issue with the disposer/finalizer in NativeTagWrapper but as you noted its simply that we aren't calling Dispose the right way from Tag and Tag finalizers. Thanks again! |
@ShadowMarker789 I am struggling to reproduce the issue. Are you able to reliably produce this problem and if so can you put together a test case? Of course we could just implement the Dispose pattern, but without having a bug repro, how do we know we've fixed the right thing? This is my attempt at a repro case, which doesn't throw in the finalizer. Action dispose = () =>
{
using var tag = new Tag()
{
Gateway = "127.0.0.1",
Path = "1,0",
PlcType = PlcType.ControlLogix,
Protocol = Protocol.ab_eip,
Name = "MyNonExistentTag",
Timeout = TimeSpan.FromSeconds(1)
};
try
{
// Regardless of whether Initialize() is run or not our finalizer doesn't throw
tag.Initialize(); // Fails because our tag doesn't exist. But we catch so we can continue onto our Disposer and Finalizer.
}
catch
{
}
// Regardless of whether or not there is a call to Dispose(), the finalizer doesn't throw.
tag.Dispose();
};
dispose();
GC.Collect(0, GCCollectionMode.Forced);
GC.WaitForPendingFinalizers(); // Run finalizer, but shouldn't run any cleanup method because it is either already disposed, or was never initialized. |
I'll see if I can pen in some time later for a test, but I usually grab libplctag.Net from a Nuget package. Is there a prerelease version I can grab that has the intended change for me to test? |
@ShadowMarker789 - I haven't made any changes to the code, so far I'm trying to reproduce the problem. Do you have some code that reliably reproduces the problem? |
I have hit it ... once again. There seems to be some weird sequence you must enter and do to die like this. Just letting you know that I'm working on reproducing it.
|
The repro process is very annoying. It requires a way to deliberately strain the network to make tags timeout, such as clumsy Use the following filter for CIP/EtherNet/IP traffic You will want to alternate between having it drop all packets, and have it drop no packets. Have it 10 seconds fine, 10 seconds drop. On the console window (since this is a console application) press the enter key every second or so, changing whether we're sending tags as eligible for collection or not. Sometimes it takes a minute or two, but then I hit the exception above with the above call stack. You need one tag that DOES exist, and one tag that does not. Program.cs
|
@ShadowMarker789 found the bug! Thanks so much for doing the work on this. I was able to distill your repro code down to its simplest form as follows: var tag = new Tag()
{
Gateway = "127.0.0.1",
Path = "1,0",
PlcType = PlcType.ControlLogix,
Protocol = Protocol.ab_eip,
Name = "MyNonExistentTag",
Timeout = TimeSpan.FromSeconds(5)
};
await tag.InitializeAsync(); // Still sets internal isInitialized despite failing!! It should throw but doesn't.
tag.Dispose(); // Because it thinks it has been initialized, it tries to remove all callbacks, etc The bug is that there should be an error thrown during InitializeAsync here like there is for ReadAsync and WriteAsync. I think this would have been introduced when we made use of the CREATED event in underlying libplctag in b2016ab |
This is closed by #254 right? |
Yes |
This caught me by surprise, found it in the Debug output log in Visual Studio
For libplctag v 1.0.13
Do you want to do anything about this? I'm not sure what the implications are for throwing unhandled exceptions in the finalizer for a class.
The text was updated successfully, but these errors were encountered: