private static void GrowOverlappedDataCache()
{
OverlappedDataCacheLine data = new OverlappedDataCacheLine();
if (m_overlappedDataCache == null)
{
// Add the first node in the list
if (Interlocked.CompareExchange<OverlappedDataCacheLine>(ref m_overlappedDataCache, data, null) == null)
{
// Use GC to remove items.
new OverlappedDataCache();
return;
}
}
// If there is already a first node in the list we'll add the new OverlappedDataCacheLine at the end
if (m_cleanupObjectCount == 0)
{
new OverlappedDataCache();
}
while (true)
{
// Chain the new node
OverlappedDataCacheLine walk = m_overlappedDataCache;
while (null != walk && null != walk.m_next)
{
// There's a race with the finalizer here between testing if walk.m_next is null and assigning
// Note 1.
// If walk has been removed from the list by the finalizer thread we still have a valid
// walk.next chain that will eventually lead us back to the original list, or to NULL.
// If NULL see Note 4 below.
// Note 2.
// If walk.next was removed from list the next time through the loop we'll be in the
// situation described in Note 1.
// Note 3.
// If walk.next was set to NULL by the finalizer thread we'll need to test for that in the
// while condition and after exiting the loop.
walk = walk.m_next;
}
// if walk has become null (due to finalizer race) after the while test
// simply return and let GetOverlappedData retry!
if (null == walk)
return;
// Add the new OverlappedDataCacheLine at the end of the list.
// Note 4.
// Even if the node that walk points to has been removed from the list we simply
// add the new node to an unreachable graph. GetOverlappedData() will notice that
// there are still no empty OverlappedData elements and call us again. The
// "unreachable list" will be reclaimed during the next GC.
if (Interlocked.CompareExchange<OverlappedDataCacheLine>(ref walk.m_next, data, null) == null)
break;
}
}