private void AddIter(InternalTransaction txNew)
{
//
// Theory of operation.
//
// Note that the head bucket contains any transaction with essentially infinite
// timeout (long.MaxValue). The list is sorted in decending order. To add
// a node the code must walk down the list looking for a set of bucket that matches
// the absolute timeout value for the transaction. When it is found it passes
// the insert down to that set.
//
// An importent thing to note about the list is that forward links are all weak
// references and reverse links are all strong references. This allows the GC
// to clean up old links in the list so that they don't need to be removed manually.
// However if there is still a rooted strong reference to an old link in the
// chain that link wont fall off the list because there is a strong reference held
// forward.
//
BucketSet currentBucketSet = _headBucketSet;
while (currentBucketSet.AbsoluteTimeout != txNew.AbsoluteTimeout)
{
BucketSet lastBucketSet = null;
do
{
WeakReference nextSetWeak = (WeakReference)currentBucketSet.nextSetWeak;
BucketSet nextBucketSet = null;
if (nextSetWeak != null)
{
nextBucketSet = (BucketSet)nextSetWeak.Target;
}
if (nextBucketSet == null)
{
//
// We've reached the end of the list either because nextSetWeak was null or
// because its reference was collected. This code doesn't care. Make a new
// set, attempt to attach it and move on.
//
BucketSet newBucketSet = new BucketSet(this, txNew.AbsoluteTimeout);
WeakReference newSetWeak = new WeakReference(newBucketSet);
WeakReference oldNextSetWeak = (WeakReference)Interlocked.CompareExchange(
ref currentBucketSet.nextSetWeak, newSetWeak, nextSetWeak);
if (oldNextSetWeak == nextSetWeak)
{
// Ladies and Gentlemen we have a winner.
newBucketSet.prevSet = currentBucketSet;
}
// Note that at this point we don't update currentBucketSet. On the next loop
// iteration we should be able to pick up where we left off.
}
else
{
lastBucketSet = currentBucketSet;
currentBucketSet = nextBucketSet;
}
}
while (currentBucketSet.AbsoluteTimeout > txNew.AbsoluteTimeout);
if (currentBucketSet.AbsoluteTimeout != txNew.AbsoluteTimeout)
{
//
// Getting to here means that we've found a slot in the list where this bucket set should go.
//
BucketSet newBucketSet = new BucketSet(this, txNew.AbsoluteTimeout);
WeakReference newSetWeak = new WeakReference(newBucketSet);
newBucketSet.nextSetWeak = lastBucketSet.nextSetWeak;
WeakReference oldNextSetWeak = (WeakReference)Interlocked.CompareExchange(
ref lastBucketSet.nextSetWeak, newSetWeak, newBucketSet.nextSetWeak);
if (oldNextSetWeak == newBucketSet.nextSetWeak)
{
// Ladies and Gentlemen we have a winner.
if (oldNextSetWeak != null)
{
BucketSet oldSet = (BucketSet)oldNextSetWeak.Target;
if (oldSet != null)
{
// prev references are just there to root things for the GC. If this object is
// gone we don't really care.
oldSet.prevSet = newBucketSet;
}
}
newBucketSet.prevSet = lastBucketSet;
}
// Special note - We are going to loop back to the BucketSet that preceeds the one we just tried
// to insert because we may have lost the race to insert our new BucketSet into the list to another
// "Add" thread. By looping back, we check again to see if the BucketSet we just created actually
// got added. If it did, we will exit out of the outer loop and add the transaction. But if we
// lost the race, we will again try to add a new BucketSet. In the latter case, the BucketSet
// we created during the first iteration will simply be Garbage Collected because there are no
// strong references to it since we never added the transaction to a bucket and the act of
// creating the second BucketSet with remove the backward reference that was created in the
// first trip thru the loop.
currentBucketSet = lastBucketSet;
lastBucketSet = null;
// The outer loop will iterate and pick up where we left off.
}
}
//
// Great we found a spot.
//
currentBucketSet.Add(txNew);
}