Reputation: 132618
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
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