internal Bucket(BucketSet owningSet) { _timedOut = false; _index = -1; _size = 1024; // A possible design change here is to have this scale dynamically based on load. _transactions = new InternalTransaction[_size]; _owningSet = owningSet; }
internal TransactionTable() { this.timer = new Timer(new TimerCallback(this.ThreadTimer), null, -1, this.timerInterval); this.timerEnabled = false; this.timerInterval = 0x200; this.ticks = 0L; this.headBucketSet = new BucketSet(this, 0x7fffffffffffffffL); this.rwLock = new CheapUnfairReaderWriterLock(); }
private void AddIter(InternalTransaction txNew) { BucketSet headBucketSet = this.headBucketSet; while (headBucketSet.AbsoluteTimeout != txNew.AbsoluteTimeout) { BucketSet set3 = null; do { WeakReference nextSetWeak = (WeakReference) headBucketSet.nextSetWeak; BucketSet target = null; if (nextSetWeak != null) { target = (BucketSet) nextSetWeak.Target; } if (target == null) { BucketSet set6 = new BucketSet(this, txNew.AbsoluteTimeout); WeakReference reference5 = new WeakReference(set6); WeakReference reference4 = (WeakReference) Interlocked.CompareExchange(ref headBucketSet.nextSetWeak, reference5, nextSetWeak); if (reference4 == nextSetWeak) { set6.prevSet = headBucketSet; } } else { set3 = headBucketSet; headBucketSet = target; } } while (headBucketSet.AbsoluteTimeout > txNew.AbsoluteTimeout); if (headBucketSet.AbsoluteTimeout != txNew.AbsoluteTimeout) { BucketSet set2 = new BucketSet(this, txNew.AbsoluteTimeout); WeakReference reference3 = new WeakReference(set2); set2.nextSetWeak = set3.nextSetWeak; WeakReference reference2 = (WeakReference) Interlocked.CompareExchange(ref set3.nextSetWeak, reference3, set2.nextSetWeak); if (reference2 == set2.nextSetWeak) { if (reference2 != null) { BucketSet set5 = (BucketSet) reference2.Target; if (set5 != null) { set5.prevSet = set2; } } set2.prevSet = headBucketSet; } headBucketSet = set3; set3 = null; } } headBucketSet.Add(txNew); }
private void AddIter(InternalTransaction txNew) { BucketSet headBucketSet = this.headBucketSet; while (headBucketSet.AbsoluteTimeout != txNew.AbsoluteTimeout) { BucketSet set3 = null; do { WeakReference nextSetWeak = (WeakReference)headBucketSet.nextSetWeak; BucketSet target = null; if (nextSetWeak != null) { target = (BucketSet)nextSetWeak.Target; } if (target == null) { BucketSet set6 = new BucketSet(this, txNew.AbsoluteTimeout); WeakReference reference5 = new WeakReference(set6); WeakReference reference4 = (WeakReference)Interlocked.CompareExchange(ref headBucketSet.nextSetWeak, reference5, nextSetWeak); if (reference4 == nextSetWeak) { set6.prevSet = headBucketSet; } } else { set3 = headBucketSet; headBucketSet = target; } }while (headBucketSet.AbsoluteTimeout > txNew.AbsoluteTimeout); if (headBucketSet.AbsoluteTimeout != txNew.AbsoluteTimeout) { BucketSet set2 = new BucketSet(this, txNew.AbsoluteTimeout); WeakReference reference3 = new WeakReference(set2); set2.nextSetWeak = set3.nextSetWeak; WeakReference reference2 = (WeakReference)Interlocked.CompareExchange(ref set3.nextSetWeak, reference3, set2.nextSetWeak); if (reference2 == set2.nextSetWeak) { if (reference2 != null) { BucketSet set5 = (BucketSet)reference2.Target; if (set5 != null) { set5.prevSet = set2; } } set2.prevSet = headBucketSet; } headBucketSet = set3; set3 = null; } } headBucketSet.Add(txNew); }
internal TransactionTable() { // Create a timer that is initially disabled by specifing an Infinite time to the first interval _timer = new Timer(new TimerCallback(ThreadTimer), null, Timeout.Infinite, _timerInterval); // Note that the timer is disabled _timerEnabled = false; // Store the timer interval _timerInterval = 1 << TransactionTable.timerInternalExponent; // Ticks start off at zero. _ticks = 0; // The head of the list is long.MaxValue. It contains all of the transactions that for // some reason or other don't have a timeout. _headBucketSet = new BucketSet(this, long.MaxValue); // Allocate the lock _rwLock = new CheapUnfairReaderWriterLock(); }
// Process a timer event private void ThreadTimer(Object state) { // // Theory of operation. // // To timeout transactions we must walk down the list starting from the head // until we find a link with an absolute timeout that is greater than our own. // At that point everything further down in the list is elegable to be timed // out. So simply remove that link in the list and walk down from that point // timing out any transaction that is found. // // There could be a race between this callback being queued and the timer // being disabled. If we get here when the timer is disabled, just return. if (!_timerEnabled) { return; } // Increment the number of ticks _ticks++; _lastTimerTime = DateTime.UtcNow.Ticks; // // First find the starting point of transactions that should time out. Every transaction after // that point will timeout so once we've found it then it is just a matter of traversing the // structure. // BucketSet lastBucketSet = null; BucketSet currentBucketSet = _headBucketSet; // The list always has a head. // Acquire a writer lock before checking to see if we should disable the timer. // Adding of transactions acquires a reader lock and might insert a new BucketSet. // If that races with our check for a BucketSet existing, we may not timeout that // transaction that is being added. WeakReference nextWeakSet = null; BucketSet nextBucketSet = null; nextWeakSet = (WeakReference)currentBucketSet.nextSetWeak; if (nextWeakSet != null) { nextBucketSet = (BucketSet)nextWeakSet.Target; } if (nextBucketSet == null) { _rwLock.EnterWriteLock(); try { // Access the nextBucketSet again in writer lock to account for any race before disabling the timeout. nextWeakSet = (WeakReference)currentBucketSet.nextSetWeak; if (nextWeakSet != null) { nextBucketSet = (BucketSet)nextWeakSet.Target; } if (nextBucketSet == null) { // // Special case to allow for disabling the timer. // // If there are no transactions on the timeout list we can disable the // timer. if (!_timer.Change(Timeout.Infinite, Timeout.Infinite)) { throw TransactionException.CreateInvalidOperationException( SR.TraceSourceLtm, SR.UnexpectedTimerFailure, null ); } _timerEnabled = false; return; } } finally { _rwLock.ExitWriteLock(); } } // Note it is slightly subtle that we always skip the head node. This is done // on purpose because the head node contains transactions with essentially // an infinite timeout. do { do { nextWeakSet = (WeakReference)currentBucketSet.nextSetWeak; if (nextWeakSet == null) { // Nothing more to do. return; } nextBucketSet = (BucketSet)nextWeakSet.Target; if (nextBucketSet == null) { // Again nothing more to do. return; } lastBucketSet = currentBucketSet; currentBucketSet = nextBucketSet; }while (currentBucketSet.AbsoluteTimeout > _ticks); // // Pinch off the list at this point making sure it is still the correct set. // // Note: We may lose a race with an "Add" thread that is inserting a BucketSet in this location in // the list. If that happens, this CompareExchange will not be performed and the returned abortingSetsWeak // value will NOT equal nextWeakSet. But we check for that and if this condition occurs, this iteration of // the timer thread will simply return, not timing out any transactions. When the next timer interval // expires, the thread will walk the list again, find the appropriate BucketSet to pinch off, and // then time out the transactions. This means that it is possible for a transaction to live a bit longer, // but not much. WeakReference abortingSetsWeak = (WeakReference)Interlocked.CompareExchange(ref lastBucketSet.nextSetWeak, null, nextWeakSet); if (abortingSetsWeak == nextWeakSet) { // Yea - now proceed to abort the transactions. BucketSet abortingBucketSets = null; do { if (abortingSetsWeak != null) { abortingBucketSets = (BucketSet)abortingSetsWeak.Target; } else { abortingBucketSets = null; } if (abortingBucketSets != null) { abortingBucketSets.TimeoutTransactions(); abortingSetsWeak = (WeakReference)abortingBucketSets.nextSetWeak; } }while (abortingBucketSets != null); // That's all we needed to do. break; } // We missed pulling the right transactions off. Loop back up and try again. currentBucketSet = lastBucketSet; }while (true); }
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); }
private void ThreadTimer(object state) { if (!this.timerEnabled) { return; } this.ticks += 1L; this.lastTimerTime = DateTime.UtcNow.Ticks; BucketSet set4 = null; BucketSet headBucketSet = this.headBucketSet; WeakReference nextSetWeak = (WeakReference) headBucketSet.nextSetWeak; BucketSet target = null; if (nextSetWeak != null) { target = (BucketSet) nextSetWeak.Target; } if (target == null) { this.rwLock.AcquireWriterLock(); try { if (!this.timer.Change(-1, -1)) { throw TransactionException.CreateInvalidOperationException(System.Transactions.SR.GetString("TraceSourceLtm"), System.Transactions.SR.GetString("UnexpectedTimerFailure"), null); } this.timerEnabled = false; return; } finally { this.rwLock.ReleaseWriterLock(); } } Label_00A0: nextSetWeak = (WeakReference) headBucketSet.nextSetWeak; if (nextSetWeak != null) { target = (BucketSet) nextSetWeak.Target; if (target != null) { set4 = headBucketSet; headBucketSet = target; if (headBucketSet.AbsoluteTimeout <= this.ticks) { Thread.BeginCriticalRegion(); try { WeakReference reference2 = (WeakReference) Interlocked.CompareExchange(ref set4.nextSetWeak, null, nextSetWeak); if (reference2 == nextSetWeak) { BucketSet set = null; do { if (reference2 != null) { set = (BucketSet) reference2.Target; } else { set = null; } if (set != null) { set.TimeoutTransactions(); reference2 = (WeakReference) set.nextSetWeak; } } while (set != null); return; } } finally { Thread.EndCriticalRegion(); } headBucketSet = set4; } goto Label_00A0; } } }
private void ThreadTimer(object state) { if (!this.timerEnabled) { return; } this.ticks += 1L; this.lastTimerTime = DateTime.UtcNow.Ticks; BucketSet set4 = null; BucketSet headBucketSet = this.headBucketSet; WeakReference nextSetWeak = (WeakReference)headBucketSet.nextSetWeak; BucketSet target = null; if (nextSetWeak != null) { target = (BucketSet)nextSetWeak.Target; } if (target == null) { this.rwLock.AcquireWriterLock(); try { if (!this.timer.Change(-1, -1)) { throw TransactionException.CreateInvalidOperationException(System.Transactions.SR.GetString("TraceSourceLtm"), System.Transactions.SR.GetString("UnexpectedTimerFailure"), null); } this.timerEnabled = false; return; } finally { this.rwLock.ReleaseWriterLock(); } } Label_00A0: nextSetWeak = (WeakReference)headBucketSet.nextSetWeak; if (nextSetWeak != null) { target = (BucketSet)nextSetWeak.Target; if (target != null) { set4 = headBucketSet; headBucketSet = target; if (headBucketSet.AbsoluteTimeout <= this.ticks) { Thread.BeginCriticalRegion(); try { WeakReference reference2 = (WeakReference)Interlocked.CompareExchange(ref set4.nextSetWeak, null, nextSetWeak); if (reference2 == nextSetWeak) { BucketSet set = null; do { if (reference2 != null) { set = (BucketSet)reference2.Target; } else { set = null; } if (set != null) { set.TimeoutTransactions(); reference2 = (WeakReference)set.nextSetWeak; } }while (set != null); return; } } finally { Thread.EndCriticalRegion(); } headBucketSet = set4; } goto Label_00A0; } } }
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 = this.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 ---- 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 ----, 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 ); }
internal TransactionTable() { // Create a timer that is initially disabled by specifing an Infinite time to the first interval this.timer = new Timer( new TimerCallback(ThreadTimer), null, Timeout.Infinite, this.timerInterval ); // Note that the timer is disabled this.timerEnabled = false; // Store the timer interval this.timerInterval = 1 << TransactionTable.timerInternalExponent; // Ticks start off at zero. this.ticks = 0; // The head of the list is long.MaxValue. It contains all of the transactions that for // some reason or other don't have a timeout. this.headBucketSet = new BucketSet( this, long.MaxValue ); // Allocate the lock rwLock = new CheapUnfairReaderWriterLock(); }
internal Bucket(BucketSet owningSet) { this.transactions = new InternalTransaction[this.size]; this.owningSet = owningSet; }