// 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 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; } } }