klewgenius
klewgenius

Reputation: 125

How to prevent this race condition?

We have a Client class which extends a BaseClass.

The BaseClass has this methods:

    protected void Proxy()
    {
        Error = null;
        _proxy = new WebServiceClient<T>(_fullURL);
        Error = _proxy.Error;
    }

    protected virtual void Cleanup()
    {
        if (_proxy != null)
        {
            _proxy.Dispose();
            _proxy = null;
        }
    }

And the Client contains several operations that are being called in parallel. Client is not a Singleton, we generate one instance every time.

The operations are like:

public void OperationAsync(Action<BaseResult> callback)
{

    TaskCompletionSource<String> taskSrc = new TaskCompletionSource<String>();
    Task<String> tsk = taskSrc.Task;
    try
    {
        Proxy();

        ThreadPool.QueueUserWorkItem(t =>
        {
            try
            {
                String result = _proxy.Channel.ExecuteOperation(SecurityToken());
                taskSrc.SetResult(result);
            }
            catch (Exception ex)
            {
                taskSrc.SetException(ex);
            }
        });

        tsk.Wait();

        BaseResult r = new BaseResult();
        r.Value = tsk.Result;
        r.Error = tsk.Exception;
        Cleanup();

        if (callback != null)
        {
            callback(r);
        }

    }
    catch (Exception ex)
    {
        FileManager.Log(ex);
    }
}

As you can see, each operation calls the Proxy and CleanUp operations.

We did not discover any pattern of behavior yet, but sometimes (maybe once a day) we see this error in the log file:

One or more errors occurred.. InnerException: System.ObjectDisposedException: Cannot access a disposed object. Object name: 'System.ServiceModel.Channels.ServiceChannel'.

It doesn´t happen on any specific operation. It varies all times. I believe that the Proxy needs to be done during the constructor and the CleanUp during the dispose, but it implies to change several things and I want to be sure.

I would really appreciate any idea on how can improve it.

Upvotes: 1

Views: 726

Answers (1)

Enigmativity
Enigmativity

Reputation: 117084

Since you original code was calling tsk.Wait(); you were blocking the calling thread while you run your proxy code on a background thread. There's no benefit and likely increased overhead doing it like this.

So, here's how to prevent the race condition:

public void OperationAsync(Action<BaseResult> callback)
{
    try
    {
        var r = new BaseResult();
        using (var proxy = new WebServiceClient<T>(_fullURL))
        {
            try
            {
                r.Value = proxy.Channel.ExecuteOperation(SecurityToken());
            }
            catch (Exception ex)
            {
                r.Error = ex;
            }
        }
        if (callback != null)
        {
            callback(r);
        }
    }
    catch (Exception ex)
    {
        FileManager.Log(ex);
    }
}

Upvotes: 1

Related Questions