Let’s look over the following code and see if we can find the bug. When the Callback method is called, the _callbackAction field is null all the time (except 2-3 exception).
Well, the root cause of the problem is ‘Stop’ method. If we look closely you will see that the callback field is set to NULL when the results comes.
This is the kind of bug where you can spend 2-3 hours, trying to understand why you cannot access local fields from other thread, looking over AppDomain, MarshalByRefObject and what the hell is panning.
What you should never do: You should never use the action that is executed in the Dispose in other method because you know that resources are disposed. You could dispose more than you want.
The code should look like this this (but we still use the 'Dispose' method for Stop):
public class FooReader: IDisposable
{
private ExternalComResource _externalResource;
private Action<Foo> _callbackAction;
public void Start(Action<Foo> callbackAction)
{
lock (this)
{
_callbackAction = callbackAction;
_externalResource = new ExternalComResource();
...
_externalResource.Callback += Callback;
_externalResource.Start(1);
}
}
public void Stop()
{
StopAndReleaseResources();
}
private void Callback(object source, EventArgs e)
{
...
StopCapture();
...
if (_callbackAction != null)
{
_callbackAction.Invoke(someData);
}
...
}
public void Dispose()
{
StopAndReleaseResources();
}
private void StopAndReleaseResources()
{
if (_externalResource != null)
{
lock (this)
{
if (_externalResource != null)
{
_externalResource.Stop();
_externalResource.Dispose();
_externalResource = null;
_callbackAction = null;
}
}
}
}
}
Did you spotted the problem?Well, the root cause of the problem is ‘Stop’ method. If we look closely you will see that the callback field is set to NULL when the results comes.
This is the kind of bug where you can spend 2-3 hours, trying to understand why you cannot access local fields from other thread, looking over AppDomain, MarshalByRefObject and what the hell is panning.
What you should never do: You should never use the action that is executed in the Dispose in other method because you know that resources are disposed. You could dispose more than you want.
The code should look like this this (but we still use the 'Dispose' method for Stop):
public class FooReader: IDisposable
{
private ExternalComResource _externalResource;
private Action<Foo> _callbackAction;
public void Start(Action<Foo> callbackAction)
{
lock (this)
{
_callbackAction = callbackAction;
_externalResource = new ExternalComResource();
...
_externalResource.Callback += Callback;
_externalResource.Start(1);
}
}
public void Stop()
{
StopAndReleaseResources();
}
private void Callback(object source, EventArgs e)
{
...
StopCapture();
...
if (_callbackAction != null)
{
_callbackAction.Invoke(someData);
}
...
}
public void Dispose()
{
StopAndReleaseResources();
}
private void StopAndReleaseResources()
{
if (_externalResource != null)
{
lock (this)
{
if (_externalResource != null)
{
_externalResource.Stop();
_externalResource.Dispose();
_externalResource = null;
//_callbackAction = null;
}
}
}
}
}
Comments
Post a Comment