Rachel
Rachel

Reputation: 132618

Could this usage of AutoResetEvent be causing the issue described?

I am not used to working with AutoResetEvent, and am wondering how the following could occur.

We have a log file that is consistently recording a different order of loading operations on two different machines :

Machine A (problem occurring)

Loading Module A
Loading Module B
Loading Shell.
Show Window.
Waiting Until Services are Loaded & Initialized.
Loading Module C
Starting Application ..

Machine B (normal log file)

Loading Module A
Loading Module B
Loading Module C
Starting Application ..
Loading Shell.
Show Window.
Initializing Menu Elements ...

The first machine never loads the Menu Elements.

The code is something like this :

public class SplashScreen : Form
{
    // SplashScreen.Run method which signals the ProgressCompleted event
    private void Run()
    {
        // some code

        MySingleton.Instance.ExecutionCompleted.WaitOne();
        MySingleton.Instance.Completed = true;
        MySingleton.Instance.ProgressCompleted.Set();

        // more code
    }
}

public class ShellApplication : FormShellApplication<WorkItem, ShellForm>
{    
    // ShellApplication.Start override
    protected override void Start()
    {
        MySingleton.Instance.ProgressCompleted.WaitOne();
        WriteToLog("Starting Application ..");
        base.Start();
    }
}

public class ShellForm : Form
{    
    // Background worker that runs from ShellForm.Load
    private void worker_DoWork(object sender, DoWorkEventArgs e)
    {
        // check to ensure services & ui components are loaded & initialized 
        if (!MySingleton.Instance.Completed)
        {
            WriteToLog("Waiting Until Services are Loaded & Initialized.");
            MySingleton.Instance.ProgressCompleted.WaitOne();
        }
    }

    // Background worker’s RunWorkerCompleted that runs from ShellForm.Load
    private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        // Some Code
        WriteToLog("Initializing Menu Elements ...");
    }
}

The singleton uses an AutoResetEvent for ExecutionCompleted and ProgressCompleted, and a bool with a sync lock for the Completed flag

public class MySingleton
{
    private object _sync = new object();

    private static AutoResetEvent _executionCompleted = new AutoResetEvent(false);
    private static AutoResetEvent _progressCompleted = new AutoResetEvent(false);
    private static bool _completed = false;

    public AutoResetEvent ProgressCompleted
    {
        get
        {
            lock (_sync)
            {
                return _progressCompleted;
            }
        }
    }

    public AutoResetEvent ExecutionCompleted
    {
        get 
        {
            lock (_sync)
            {
                return _executionCompleted;
            }
        }
    }

    public bool Completed
    {
        get
        {
            lock (_sync)
            {
                return _completed;
            }
        }
        set
        {
            lock (_sync)
            {
                _completed = value;
            }
        }
    }
}

From what I can tell, it looks like on Machine A the background worker starts before all the modules load, and ProgressCompleted.WaitOne() is either never hit or is hit while the .Completed flag is false. I don't have enough experience with AutoResetEvent to know if anything in the code would be causing this, such as the second .WaitOne call to it.

I don't think it's a timing issue related to the sync lock because it consistently happens every single time on Machine A, while I am unable to reproduce the problem on Machine B (my machine).

Is there anything in the code above related to the AutoResetEvent usage that could cause this abnormal behavior? I am using .Net 3.5.

Upvotes: 1

Views: 158

Answers (1)

tzachs
tzachs

Reputation: 5029

You might want to consider using ManualResetEvent instead of AutoResetEvent.

Reset events are like a stoplight. Since you initialize it with false it starts as a red light. When you set it, it turns to green. The difference between ManualResetEvent and AutoResetEvent is how much time it stays on green...

ManualResetEvent will stay on green until somebody calls Reset on it. AutoResetEvent will let one car enter and automatically revert to a red light again. You set ProgressCompleted once but you wait on it twice, first time in ShellApplication.Start, and the second time in worker_DoWork. Meaning the second wait will wait forever.

The reason it won't always fail is because of the if (!MySingleton.Instance.Completed) which dictates that you don't wait for the reset event the second time if the splash screen code was faster than the background worker.

Upvotes: 2

Related Questions