internal void Wait()
{
// Easy case, we're already finished
if (1 == m_hasCompleted)
{
return;
}
// Wait on an existing event if it's available
if (null != m_event)
{
WaitOnEvent(m_event);
}
ManualResetEvent createdEvent = null;
ManualResetEvent originalEvent = null;
try
{
createdEvent = new ManualResetEvent(false);
originalEvent = Interlocked.CompareExchange<ManualResetEvent>(ref m_event, createdEvent, null);
// There are 2 race conditions that we can encounter at this point.
// 1) Two separate threads created event objects. In that case if we are the second
// such thread the "originalEvent" variable will be non-null. Destroy the created
// event and wait on the first event
// 2) Between our original completion check and the time we set the event OnCompleted
// was called. Now that the event has been created re-check the has completed value
// and destroy the event if necessary
if (null != originalEvent)
{
// Another thread got here before us. Destroy the created event and wait on the original
createdEvent.Close();
createdEvent = null; // So it doesn't get re-disposed
WaitOnEvent(originalEvent);
}
else if (1 == m_hasCompleted)
{
createdEvent.Close();
createdEvent = null; // So it doesn't get re-disposed
}
else
{
createdEvent.WaitOne();
}
}
finally
{
// If the created event is m_event we need to swap it out for null
if (null == originalEvent)
{
Interlocked.Exchange(ref m_event, null);
}
if (null != createdEvent)
{
createdEvent.Close();
}
}
}