Dispose while waiting for async result

+1 vote
asked Apr 21, 2011 by Ron Klein (180 points)
edited Apr 21, 2011

I'm using Rebex FTP library (plain old FTP, no security needed). I'm working asynchronously, in a multi-threaded environment, so the code in my class looks pretty much like this:

public class MyDownloader : IDisposable
{
    private readonly Ftp ftp = new Ftp();
    private MemoryStream ms = new MemoryStream();

    public void Download()
    {
        // connect, login, etc.            
        object state = null;
        ftp.BeginGetFile("remote/path", ms, MyCallback, state);
    }

    private void MyCallback(IAsyncResult ar)
    {
        try
        {
            var size = ftp.EndGetFile(ar);
        }
        catch
        {
            // log, whatever
        }

    }

    public void Dispose()
    {
        // disconnect if needed
        ftp.Dispose();
    }
}

The Questions

Let's say that there's a so-called race between 2 threads: Thread A calls the Dispose method in my class, and Thread B invokes the callback method (MyCallback) on the same time.

  1. Should I write my own mechanism for checking the state of my class (disposed or not)?
  2. Is it possible that after calling FTP's Dispose method, my class' MyCallback method will be executed at all?

Thanks!

Applies to: Rebex FTP/SSL

2 Answers

+1 vote
answered Apr 21, 2011 by Lukas Matyska (55,430 points)
edited Apr 21, 2011
 
Best answer

To 1.) If the Ftp object was disposed before finishing the BeginXXX method, an FtpException is thrown when calling the EndXXX method. If the Ftp object was disposed after finishing the BeginXXX method, call of the EndXXX method will succeed without an exception. I suggest you to add the following catch to handle ObjectDisposedException in this case:

catch (FtpException ex)
{
    if (ex.Status == FtpExceptionStatus.AsyncError && ex.InnerException != null && ex.InnerException is ObjectDisposedException)
        // handle background ObjectDisposedException
}

To 2.) Ones the BeginXXX method was successfully called, the callback method will be executed always (even the object has been already disposed). If an exception occurred during the background operation, calling the EndXXX method will throw an FtpException with the FtpExceptionStatus.AsyncError.

commented Apr 21, 2011 by Ron Klein (180 points)
edited Apr 21, 2011

I understand your answer, but it raises another question: Regarding the BeginGetFile method of the Ftp class - is it thread-safe? I mean, if two different threads invoke BeginGetFile and Dispose methods - is there a race?

+1 vote
answered Apr 21, 2011 by Lukas Matyska (55,430 points)
edited Apr 21, 2011

The whole Ftp class is thread safe. Let suppose 3 cases:

  1. thread A invokes BeginGetFile, then thread B invokes Dispose (in next instruction). Result: no data are transferred and a callback is executed. Calling the EndGetFile method throws an FtpException as described in the previous answer.
  2. thread B invokes Dispose, then thread A invokes BeginGetFile (in next instruction). Result: calling the BeginGetFile method in thread A throws an ObjectDisposedException.
  3. thread A invokes BeginGetFile, then thread B also invokes BeginGetFile. Result: calling the BeginGetFile method in thread B throws an FtpException with the FtpExceptionStatus.Pending. Thread A will continue and data will be transferred.
commented Apr 21, 2011 by Ron Klein (180 points)
edited Apr 21, 2011

Thanks! :-)

...